• COCI2021-2022#1 Logičari


    P7929 [COCI2021-2022#1] Logičari

    题目大意

    给你一个有 n n n个点的基环树,现在对这个基环树上的点染色,使得每个点都有且仅有一个与它相连的点(不包括他自己)被染色,求最少的染色点数。如果不存在染色方案,则输出 − 1 -1 1

    3 ≤ n ≤ 1 0 5 3\leq n\leq 10^5 3n105


    题解

    首先,我们知道,基环树是一棵树上加一条边所得到的。下文称这条边为特殊边,我们可以求出特殊边的两个端点 f m fm fm t o to to

    f i , 0 / 1 / 2 / 3 f_{i,0/1/2/3} fi,0/1/2/3表示 i i i的子树在对应状态下的最小染色点数,对应状态如下:

    • 0 0 0表示点 i i i不染色,且 i i i的不包括父亲的邻接点都没有被染色(如果有点 i i i在特殊边上,特殊边的另一点也为被染色,下同)
    • 1 1 1表示点 i i i不染色,且 i i i的不包括父亲的邻接点中有一个点被染色
    • 2 2 2表示点 i i i被染色,且 i i i的不包括父亲的邻接点都没有被染色
    • 3 3 3表示点 i i i被染色,且 i i i的不包括父亲的邻接点中有一个点被染色

    那么,状态为 0 0 0 2 2 2的点 i i i的父亲必须染色,状态为 1 1 1 3 3 3的点 i i i的父亲必须不染色。

    那么,直接做树型 D P DP DP即可。

    对于特殊边上的两个端点 f m fm fm t o to to,枚举它们的染色情况(枚举分别是否染色,只要枚举四次),在做树型 D P DP DP时对这两个点特殊处理一下即可。

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

    一些解释

    在代码中,在枚举特殊边上的两个端点 f m fm fm t o to to的染色情况的时候,用数组 n d nd nd来保存它们的染色情况:

    • n d i = − 1 nd_i=-1 ndi=1表示这个点不在特殊边上
    • n d i = 0 nd_i=0 ndi=0表示两个点都没被染色
    • n d i = 1 nd_i=1 ndi=1表示自己没被染色,对方被染色了
    • n d i = 2 nd_i=2 ndi=2表示自己被染色了,对方没被染色
    • n d i = 3 nd_i=3 ndi=3表示两个点都被染色了

    code

    #include
    using namespace std;
    const long long inf=1e9;
    int n,x,y,fm,to,tot=0,d[200005],l[200005],r[200005],dep[100005];
    int nd[100005];
    long long ans=inf,f[100005][4];
    void add(int xx,int yy){
    	l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
    }
    void gt(int u,int fa){
    	dep[u]=dep[fa]+1;
    	for(int i=r[u];i;i=l[i]){
    		if(d[i]==fa) continue;
    		if(dep[d[i]]){
    			if(dep[d[i]]<dep[u]){
    				fm=u;to=d[i];
    			}
    			continue;
    		}
    		gt(d[i],u);
    	}
    }
    void dfs(int u,int fa){
    	f[u][0]=0;f[u][2]=1;
    	long long v1=inf,v2=inf;
    	for(int i=r[u];i;i=l[i]){
    		if(d[i]==fa) continue;
    		if(nd[d[i]]!=-1&&nd[u]!=-1) continue;
    		dfs(d[i],u);
    		f[u][0]+=f[d[i]][1];
    		v1=min(v1,f[d[i]][3]-f[d[i]][1]);
    		f[u][2]+=f[d[i]][0];
    		v2=min(v2,f[d[i]][2]-f[d[i]][0]);
    	}
    	f[u][1]=f[u][0]+v1;
    	f[u][3]=f[u][2]+v2;
    	if(nd[u]!=-1){
    		if(nd[u]==0){
    			f[u][2]=f[u][3]=inf;
    		}
    		else if(nd[u]==1){
    			f[u][1]=f[u][0];
    			f[u][0]=f[u][2]=f[u][3]=inf;
    		}
    		else if(nd[u]==2){
    			f[u][0]=f[u][1]=inf;
    		}
    		else{
    			f[u][3]=f[u][2];
    			f[u][0]=f[u][1]=f[u][2]=inf;
    		}
    	}
    }
    void dd(int v1,int v2){
    	nd[fm]=v1;nd[to]=v2;
    	for(int i=1;i<=n;i++){
    		f[i][0]=f[i][1]=f[i][2]=f[i][3]=inf;
    	}
    	dfs(1,0);
    	ans=min(ans,min(f[1][1],f[1][3]));
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&x,&y);
    		add(x,y);add(y,x);
    	}
    	gt(1,0);
    	memset(nd,-1,sizeof(nd));
    	dd(0,0);
    	dd(1,2);
    	dd(2,1);
    	dd(3,3);
    	if(ans==inf) ans=-1;
    	printf("%d",ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
  • 相关阅读:
    【目标检测】Fast R-CNN算法实现
    Linux开发-Ubuntu软件源工具
    黑盒测试-场景法
    Ubuntu16.04 部署 TensorFlow2-GPU版本(虚拟环境)
    字节跳动java面试题,附详细答案解析
    SpringBoot + Redis +RabbitMQ 实现高并发并限时秒杀
    JAVA爱音乐网站计算机毕业设计Mybatis+系统+数据库+调试部署
    Vue3学习
    使用easyui前端框架快速构建一个crud应用
    软考 系统架构设计师系列知识点之边缘计算(3)
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133706777