AT2675 Two Trees 欧拉回路+神奇构造

原题: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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值