• 【AGC018F】Two Trees 构造 黑白染色


    题目描述

      有两棵有根树,顶点的编号都是(1)~(n)

      你要给每个点一个权值(a_i),使得对于两棵树的所有顶点(x),满足(|x)的子树的权值和(|=1)

      (nleq 100000)

    题解

      我们很容易得到只考虑一棵树时每个点的权值的奇偶性。如果只考虑(A)树时的奇偶性与只考虑(B)树时的奇偶性不同,就无解。

      现在我们构造一个(a_iin{-1,0,1})的方案。

      如果一个点的儿子个数是奇数,那么(a_i=0),否则(a_i eq 0)

      现在只考虑一棵子树。

      对于每一棵子树,一定有(2k+1)个权值是奇数的点。

      证明:因为整棵子树的权值和是奇数,所以有奇数个权值是奇数的点。

      那么可以匹配出(k)对点和剩下一个点。对于一对点((x,y)),令(a_x=-a_y)。所以整棵子树的权值和就是(pm1)。我们在(x,y)之间连一条边。对于每个点

      这样得到的图每个点的度数(leq 1)

      然后把两棵树得到的边合在一起,得到的图就是一个二分图。

      为什么?因为如果有奇环就一定会有相邻两条边来自同一棵树,但这是不可能的。所以就没有奇环。

      对于这个图黑白染色,黑色的点选(1),白色的点选(-1)

      时间复杂度:(O(n))

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<ctime>
    #include<utility>
    #include<cmath>
    #include<functional>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,ll> pll;
    void sort(int &a,int &b)
    {
    	if(a>b)
    		swap(a,b);
    }
    void open(const char *s)
    {
    #ifndef ONLINE_JUDGE
    	char str[100];
    	sprintf(str,"%s.in",s);
    	freopen(str,"r",stdin);
    	sprintf(str,"%s.out",s);
    	freopen(str,"w",stdout);
    #endif
    }
    int rd()
    {
    	int s=0,c;
    	while((c=getchar())<'0'||c>'9');
    	do
    	{
    		s=s*10+c-'0';
    	}
    	while((c=getchar())>='0'&&c<='9');
    	return s;
    }
    int upmin(int &a,int b)
    {
    	if(b<a)
    	{
    		a=b;
    		return 1;
    	}
    	return 0;
    }
    int upmax(int &a,int b)
    {
    	if(b>a)
    	{
    		a=b;
    		return 1;
    	}
    	return 0;
    }
    struct graph
    {
    	int v[200010];
    	int t[200010];
    	int h[100010];
    	int n;
    	graph()
    	{
    		n=0;
    	}
    	void add(int x,int y)
    	{
    		v[++n]=y;
    		t[n]=h[x];
    		h[x]=n;
    	}
    };
    graph g1,g2,g;
    int fa[100010];
    int fb[100010];
    int a1[100010];
    int a2[100010];
    int st[100010];
    int top;
    int b[100010];
    int c[100010];
    void dfs1(int x)
    {
    	int i;
    	for(i=g1.h[x];i;i=g1.t[i])
    		dfs1(g1.v[i]);
    	if(a1[x])
    		st[++top]=x;
    	while(top>=2)
    	{
    		g.add(st[top],st[top-1]);
    		g.add(st[top-1],st[top]);
    		top-=2;
    	}
    }
    void dfs2(int x)
    {
    	int i;
    	for(i=g2.h[x];i;i=g2.t[i])
    		dfs2(g2.v[i]);
    	if(a2[x])
    		st[++top]=x;
    	while(top>=2)
    	{
    		g.add(st[top],st[top-1]);
    		g.add(st[top-1],st[top]);
    		top-=2;
    	}
    }
    void dfs(int x,int v)
    {
    	c[x]=v;
    	b[x]=1;
    	int i;
    	for(i=g.h[x];i;i=g.t[i])
    		if(!b[g.v[i]])
    			dfs(g.v[i],-v);
    }
    int main()
    {
    //	open("agc018f");
    	int n;
    	scanf("%d",&n);
    	int i;
    	int rt1,rt2;
    	for(i=1;i<=n;i++)
    		a1[i]=a2[i]=1;
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&fa[i]);
    		if(fa[i]==-1)
    			rt1=i;
    		else
    		{
    			g1.add(fa[i],i);
    			a1[fa[i]]^=1;
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&fb[i]);
    		if(fb[i]==-1)
    			rt2=i;
    		else
    		{
    			g2.add(fb[i],i);
    			a2[fb[i]]^=1;
    		}
    	}
    	for(i=1;i<=n;i++)
    		if(a1[i]!=a2[i])
    		{
    			printf("IMPOSSIBLE
    ");
    			return 0;
    		}
    	printf("POSSIBLE
    ");
    	top=0;
    	dfs1(rt1);
    	top=0;
    	dfs2(rt2);
    	for(i=1;i<=n;i++)
    		if(!b[i])
    			dfs(i,1);
    	for(i=1;i<=n;i++)
    		if(!a1[i])
    			printf("0 ");
    		else
    			printf("%d ",c[i]);
    	return 0;
    }
    
  • 相关阅读:
    Java编程技术之浅析SPI服务发现机制
    Java编程开发之浅析Java引用机制
    xmake v2.5.5 发布,支持下载集成二进制镜像包
    C/C++ 构建系统,我用 xmake
    xmake v2.5.3 发布,支持构建 linux bpf 程序和 Conda 包集成
    xmake v2.5.2 发布, 支持自动拉取交叉工具链和依赖包集成
    程序员是怎么存档并管理文件版本的?
    《大厂程序员春招实习面试漫画》第一集:基础面试
    漫画解释啥是云计算
    程序员,这个需求你是真实现不了吗?
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8513408.html
Copyright © 2020-2023  润新知