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 3≤n≤105

题解

首先,我们知道,基环树是一棵树上加一条边所得到的。下文称这条边为特殊边,我们可以求出特殊边的两个端点 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

cpp 复制代码
#include<bits/stdc++.h>
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;
}
相关推荐
CodeClimb5 分钟前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
奶香臭豆腐38 分钟前
C++ —— 模板类具体化
开发语言·c++·学习
不想当程序猿_44 分钟前
【蓝桥杯每日一题】分糖果——DFS
c++·算法·蓝桥杯·深度优先
cdut_suye1 小时前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
波音彬要多做1 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
捕鲸叉1 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
只做开心事2 小时前
C++之红黑树模拟实现
开发语言·c++
程序员老冯头3 小时前
第十五章 C++ 数组
开发语言·c++·算法
程序猿会指北4 小时前
【鸿蒙(HarmonyOS)性能优化指南】启动分析工具Launch Profiler
c++·性能优化·harmonyos·openharmony·arkui·启动优化·鸿蒙开发
无 证明9 小时前
new 分配空间;引用
数据结构·c++