A.Hashing Trees
题目大意:给定数组a[i],其中a[i]表示深度为i的节点有a[i]个,问是否存在两个不同构的树同时满足这个条件。如果有,请输出。
简要题解:有很多种构造方案。我的做法是,第一次把所有深度为i的节点都向深度为i-1的第一个节点连边。第二次,尝试分出一个深度为i的节点向深度为i-1的第二个节点连边。如果可以这样生成两棵树,则有解。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define maxn 200010
using namespace std;
vector<int> v[maxn];
int a[maxn],f[maxn];
int n,h,cnt;
bool w=;
int main()
{
scanf("%d",&h);
for (int i=;i<=h;i++) scanf("%d",&a[i]);
cnt=;v[].push_back();
for (int i=;i<=h;i++)
{
if (w && a[i]!= && v[i-].size()>)
{
w=;v[i].push_back(++cnt);f[cnt]=v[i-][];
for (int j=;j<a[i];j++) v[i].push_back(++cnt),f[cnt]=v[i-][];
}
else for (int j=;j<=a[i];j++) v[i].push_back(++cnt),f[cnt]=v[i-][];
}
if (w) puts("perfect");
else
{
puts("ambiguous");
printf("0");cnt=;
for (int i=;i<=h;i++)
{
for (int j=;j<=a[i];j++) printf(" %d",cnt);
cnt+=a[i];
}
printf("\n");
printf("0");
for (int i=;i<=h;i++)
{
for (int j=;j<v[i].size();j++) printf(" %d",f[v[i][j]]);
}
printf("\n");
}
return ;
}
B. GCD of Polynomials
题目大意:构造两个度数不超过n的多项式,使他们求GCD时,调用GCD过程的次数恰好为n。要求每一项的系数为-1或0或1。
简要题解:注意到,每调用一次GCD过程,多项式的次数都会减一。所以最后的答案多项式一定是n次和n-1次的。考虑用最初的x和1,递推到n次。我使用的做法是,每次枚举将高次多项式乘(x+(-1/0/1)),低次多项式乘-1/1,构造出新的高次多项式。最后构造出解。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
int da[]={-,-,,,,};
int db[]={,-,,-,,-};
int n,m;
int a[][],b[][];
int c[];
bool solve(int x)
{
if (x==n) return ;
x++;
for (int k=;k<;k++)
{
a[x][]=da[k]*a[x-][]+db[k]*b[x-][];
for (int i=;i<=x;i++) a[x][i]=a[x-][i-]+da[k]*a[x-][i]+db[k]*b[x-][i];
bool w=;
for (int i=;i<=x;i++) if (a[x][i]>= || a[x][i]<=-) {w=;break;}
if (w)
{
for (int i=;i<x;i++) b[x][i]=a[x-][i];
if (solve(x)) return ;
}
}
return ;
}
int main()
{
scanf("%d",&n);
a[][]=;b[][]=;
if (solve())
{
printf("%d\n",n);
for (int i=;i<=n;i++) printf("%d ",a[n][i]);printf("\n");
printf("%d\n",n-);
for (int i=;i<n;i++) printf("%d ",b[n][i]);printf("\n");
}
else puts("-1");
return ;
}
C.Bipartite Segments
题目大意:给定一个无偶环的无向图,m次询问,每次询问[l,r]中能取出多少个子区间[x,y],满足只选择[x,y]中的点和边,不会形成奇环。
简要题解:以每个位置i,都有一个最靠左的端点pre[i],满足[pre[i],i]的所有后缀都满足不形成奇环。因为原图没有偶环,所以只要形成环就不满足条件,问题简单了许多。pre[i]是单调递增的,故可以用整体二分来求。对于每组询问,只需要维护一个前缀和就可以解决。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define maxn 300010
using namespace std;
vector<int> v[maxn];
int f[maxn],pre[maxn],st[maxn];
bool vis[maxn];
long long sum1[maxn],sum2[maxn];
int n,m,T,top;
int find(int i)
{
if (f[i]==i) return i;
if (!vis[i]) st[++top]=i,vis[i]=;
return f[i]=find(f[i]);
}
void solve(int l,int r,int L,int R)
{
if (l>r) return;
if (L==R)
{
for (int i=l;i<=r;i++) pre[i]=L;
return;
}
int mid=(L+R)>>;
int ans=r+;
bool w=;
for (int i=mid;i<=r;i++)
{
int j=lower_bound(v[i].begin(),v[i].end(),mid)-v[i].begin();
for (;j<v[i].size();j++)
{
int f1=find(v[i][j]),f2=find(i);
if (f1!=f2)
{
f[f1]=f2;
if (!vis[f1]) st[++top]=f1,vis[f1]=;
}
else {w=;break;}
}
if (!w) {ans=i;break;}
}
while (top) f[st[top]]=st[top],vis[st[top]]=,top--;
solve(l,ans-,L,mid);solve(ans,r,mid+,R);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[max(x,y)].push_back(min(x,y));
}
for (int i=;i<=n;i++) sort(v[i].begin(),v[i].end());
for (int i=;i<=n;i++) f[i]=i;
solve(,n,,n);
//for (int i=1;i<=n;i++) printf("%d ",pre[i]);printf("\n");
for (int i=;i<=n;i++) sum1[i]=sum1[i-]+i,sum2[i]=sum2[i-]+(i-pre[i]+);
scanf("%d",&T);
while (T--)
{
int l,r;
scanf("%d%d",&l,&r);
long long ans=;
int now=upper_bound(pre+l,pre+r+,l)-pre;
//printf("%d\n",now);
ans=sum1[now-]-sum1[l-]-l*(now-l)*(l-)+sum2[r]-sum2[now-];
printf("%I64d\n",ans);
}
return ;
}
D.Weighting a Tree
题目大意:一个无向连通图,每个点有-n到n的权值,构造一种方案,给每条边赋值,使每个点相邻的边的权值和等于这个点的权值。
简要题解:如果这个图是个二分图,那么无论如何赋值,左右两边的和必然相等,则取原图的一棵生成树,只使用树边来进行赋值即可。如果原图不是二分图,我们可以找到一条连接同一部分的边,那么把这条边的权值赋为两部分差的一半即可。按照二分图进行分类的方法,也是很厉害了。
<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 100010
using namespace std;
int head[maxn],nxt[maxn*],to[maxn*],fa[maxn],fr[maxn];
int dep[maxn];
long long ans[maxn],w[maxn];
int n,m,num,e;
long long sum[];
void addedge(int x,int y)
{
num++;to[num]=y;nxt[num]=head[x];head[x]=num;
}
void dfs(int x)
{
sum[dep[x]&]+=w[x];
for (int p=head[x];p;p=nxt[p])
if (to[p]!=fa[x])
{
if (!dep[to[p]]) dep[to[p]]=dep[x]+,fa[to[p]]=x,fr[to[p]]=p,dfs(to[p]);
else if ((dep[x]-dep[to[p]]+)&) e=p;
}
}
void solve(int x)
{
for (int p=head[x];p;p=nxt[p])
if (fa[to[p]]==x) solve(to[p]),w[x]-=ans[p/];
ans[fr[x]/]=w[x];w[x]-=ans[fr[x]/];
}
int main()
{
num=;
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) scanf("%I64d",&w[i]);
for (int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}
dep[]=;dfs();
if ((sum[]-sum[])&) {puts("NO");return ;}
if (!e && sum[]!=sum[]) {puts("NO");return ;}
if (e && sum[]!=sum[])
{
if (dep[to[e]]&) ans[e/]=(sum[]-sum[])/,sum[]=sum[];
else ans[e/]=(sum[]-sum[])/,sum[]=sum[];
w[to[e]]-=ans[e/];
w[to[e^]]-=ans[e/];
}
solve();
puts("YES");
for (int i=;i<=m;i++) printf("%I64d\n",ans[i]);
return ;
}