【学习笔记】[AGC018F] Two Trees

21 篇文章 0 订阅
18 篇文章 0 订阅

考虑如何将两棵树紧密的关联起来。单独看一个树的构造是没有意义的。如果一个点对应两棵树上度数的奇偶性不相等那么显然无解。其次对于一棵子树,度数为奇数的节点个数一定是 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]<<' ';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值