考虑如何将两棵树紧密的关联起来。单独看一个树的构造是没有意义的。如果一个点对应两棵树上度数的奇偶性不相等那么显然无解。其次对于一棵子树,度数为奇数的节点个数一定是 2 K + 1 2K+1 2K+1个,那么我们考虑能否将所有点两两配对,分为两种颜色的边,使得一个子树内蓝边和红边的数目之差恰好为 1 1 1?
考虑构造欧拉回路,我们核心是要想到对于两个不同树中的两个相同节点连边,同时对于一颗子树上相邻的两个点连边。此时我们观察,这两棵树将点分成了两个集合,这两个集合之间相连的边就是我们想要的边,那么我们考虑这颗子树与外界相连的边包括连接两颗不同的树的边(我们想要的边)以及子树根节点到父亲的边。显然如果是指向子树内那么出子树的边恰好比进子树的边多一条,反之则恰好少一条。
那么为什么会想到欧拉回路呢?事实上,我们可以把它看成从源点出发的一条流量,这条流量最终会回到源点,那么根据流量平衡的思想,就有流出量=流入量,这样就保证了一棵子树内的 − 1 / 1 -1/1 −1/1可以抵消。
复杂度 O ( n ) O(n) O(n)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
#define ull unsigned long long
using namespace std;
const int N=2e6+5;
int n,res[N];
int rt1,rt2,d1[N],d2[N];
int head[N],nxt[N],to[N],w[N],vis[N],tot=1;
stack<int>Q;
void add(int x,int y,int z){
to[++tot]=y,w[tot]=z,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,w[tot]=z,nxt[tot]=head[y],head[y]=tot;
}
void solve(){
Q.push(1);
while(Q.size()){
int u=Q.top();
int k=head[u];while(k&&vis[k])k=nxt[k];head[u]=k;
if(k){
if(w[k])res[min(u,to[k])]=(u<to[k])?1:-1;
vis[k]=vis[k^1]=1;Q.push(to[k]);
}
else{
Q.pop();
}
}
}
int main(){
cin>>n;for(int i=1;i<=n;i++){
int j;cin>>j;if(~j)d1[i]^=1,d1[j]^=1,add(i,j,0);
else d1[i]^=1,rt1=i;
}for(int i=1;i<=n;i++){
int j;cin>>j;if(~j)d2[i]^=1,d2[j]^=1,add(i+n,j+n,0);
else d2[i]^=1,rt2=i;
}add(rt1,rt2+n,0);
for(int i=1;i<=n;i++)if(d1[i]!=d2[i]){
cout<<"IMPOSSIBLE";return 0;
}for(int i=1;i<=n;i++)if(d1[i])add(i,i+n,1);
cout<<"POSSIBLE"<<"\n";
solve();
for(int i=1;i<=n;i++)cout<<res[i]<<' ';
}