原题:https://www.luogu.org/problemnew/show/AT2675
题解:有两颗树,让你求一个序列,保证了两颗树上的每一个点的子树(包括自己)对应序列的上的数的和的绝对值为1。只让求一种方案,不妨做出合理假设,只会填1,0,-1 这个假设是合理。我们可以用1,-1凑出0。考虑是填0,还是1,-1。可以发现这些点是确定的当一个点的儿子个数+1(除根外结点的度)为奇数时就只会填1,-1,为偶时只会填0。那么无解的情况就很明显了,两颗树上的奇偶信息不相同就无解。所有偶点都填0,考虑如何填奇点。这里介绍一种神奇的构造。
做法:
1.将两颗树的根连到0
2.将两颗树对应的奇点连上
3.由于所有奇点都又连上了一条边,所以构成欧拉回路,跑一边欧拉回路,若从1树到2树,两节点+1;2到1时两节点-1
考虑这样做的正确性:
可以将欧拉回路拆成一些环
1类环表示从当前节点向儿子走,然后又从儿子走回当前点
2类环表示从当前节点向儿子走,从父亲边走回当前点(反向也是一样的)。
3类环表示从当前节点向父亲边或横叉边走,又从这两个中另一个走回来
4类环表示从当前节点向儿子走,从横叉边走回当前点(反向也是一样的)。
根据上边的做法,1,4类环都是是对答案无影响的。所以只用关注2,3类环。
当当前节点为偶点时,这个节点的权值为0,他显然没有3类环,所以只受二类环的影响,所以子树的权值和只会+1,-1
当节点为奇点,他会有1个2类环或一个3类环,子树的权值和只会+1,-1
细节:1.跑欧拉回路时要做当前弧优化
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct E{int x,y,nxt,flag;}mm[N<<2];
int h[N],len,p[N],n,ans[N],cur[N];
bool v[N];
inline int rd(){
int x=0;int f=1;char s=getchar();
while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
return x*f;
}
inline void add(int x,int y){
++len;
mm[len].x=x;mm[len].y=y;mm[len].nxt=h[x];h[x]=len;
++len;
mm[len].x=y;mm[len].y=x;mm[len].nxt=h[y];h[y]=len;
p[x]++;p[y]++;
return ;
}
void dfs(int u,int f){
for(int& k=h[u];k;k=mm[k].nxt){
int y=mm[k].y;
if(mm[k].flag||f==y) continue;
mm[k].flag=mm[k%2==0?k-1:k+1].flag=1;
if(v[u] && u<=n && y>n) ans[u]=ans[y]=1;
if(v[u] && u>n && y<=n) ans[u]=ans[y]=-1;
dfs(y,u);
}
}
int main(){
// freopen("t.in","r",stdin);
n=rd();memset(h,0,sizeof h);
for(int i=1;i<=n;i++){
int u=rd();u=max(0,u);
add(u,i);
}
for(int i=1;i<=n;i++){
int u=rd();u=u==-1?0:(u+n);
add(u,i+n);
}
for(int i=1;i<=2*n;i++){
if(p[i]%2) v[i]=1;
else v[i]=0;
}
bool flag=true;
for(int i=1;i<=n;i++)
if(v[i]!=v[i+n]) flag=0;
if(flag) printf("POSSIBLE\n");
else {
printf("IMPOSSIBLE\n");return 0;
}
for(int i=1;i<=n;i++)
if(v[i]) add(i,i+n);
dfs(1,0);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);printf("\n");
return 0;
}