BZOJ1304 [CQOI2009]叶子的染色

题目

给一棵m(m<=1e4)个结点的无根树,你可以选择一个度数大于1的结点作为根,

然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。

你的着色方案应该保证,根结点到每个叶子的简单路径上,

都至少包含一个有色结点(哪怕是这个叶子本身)。 

题目给出一个n(n<=5021),保证[1,n]均为叶子结点

对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。

给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。

输出最少的着色节点的个数。

思路来源

乱搞AC

https://www.cnblogs.com/neighthorn/p/6188302.html

题解

在换根dp专题里,就写了换根dp,但是写完发现自己非常sb

dp[u]维护的是这个点u能使得儿子v减少多少个染色的点,

所有的dp[u]之和即为所有能减少的和,n-maxsumdp[u]即为所求,

对于一棵子树u的所有子节点v来说,贪心地对u进行染色,

如果v中黑色比白色多,则染成黑色;如果少就染成白色;否则染成万能节点,表示可黑可白

然后暴力换根,u为根换成v为根的时候,变化的只是二者的黑儿子、白儿子、万能儿子的个数,

也因此带来了u和v这两个点的颜色变化,和减少的dp值的变化,别的点均不变,返祖时换回来

但是,注意到这道题中有一个性质,即选根为哪个点是不影响答案的

对于相邻的两个点(u,v)来说,二者颜色不同的话两个着色点都需要保留,

二者颜色相同的话一定会少着色一个,所以对答案没有影响,随便选个根从底往上dp即可

代码

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
typedef pair<int,int> P;
typedef long long ll;
const int N=1e4+10;//INF=0x3f3f3f3f;
vector<int>e[N];
bool ok[N];
int b[N],w[N],same[N];
int rt,n,m,u,v,sz[N],dp[N],col[N];
int res,ans;
void dfs(int u,int fa){
	sz[u]=ok[u];
	for(auto &v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		sz[u]+=sz[v];
		if(col[v]==0)b[u]++;
		else if(col[v]==1)w[u]++;
		else if(col[v]==2)same[u]++;
	}
	if(b[u]>w[u]){
		col[u]=0;
		dp[u]=b[u]+same[u]-1;
	}
	else if(b[u]<w[u]){
		col[u]=1;
		dp[u]=w[u]+same[u]-1;
	}
	else{
		col[u]=2;
		dp[u]=w[u]+same[u]-1;
	}
	ans+=dp[u];
}
void dfs2(int u,int fa){
	res=max(res,ans);
	for(auto &v:e[u]){
		if(v==fa)continue;
		if(v<=m)continue;
		int pcu=col[u],pdpu=dp[u],pbu=b[u],pwu=w[u],psu=same[u];
		int pcv=col[v],pdpv=dp[v],pbv=b[v],pwv=w[v],psv=same[v];
		int pans=ans;
		if(col[v]==0)b[u]--;
		else if(col[v]==1)w[u]--;
		else if(col[v]==2)same[u]--;
		sz[u]-=sz[v];
		ans-=dp[u];
		if(b[u]>w[u]){
			col[u]=0;
			dp[u]=b[u]+same[u]-1;
		}
		else if(b[u]<w[u]){
			col[u]=1;
			dp[u]=w[u]+same[u]-1;
		}
		else{
			col[u]=2;
			dp[u]=w[u]+same[u]-1;
		}
		ans+=dp[u];
		if(col[u]==0)b[v]++;
		else if(col[u]==1)w[v]++;
		else if(col[u]==2)same[v]++;
		sz[v]+=sz[u];
		ans-=dp[v];
		if(b[v]>w[v]){
			col[v]=0;
			dp[v]=b[v]+same[v]-1;
		}
		else if(b[v]<w[v]){
			col[v]=1;
			dp[v]=w[v]+same[v]-1;
		}
		else{
			col[v]=2;
			dp[v]=w[v]+same[v]-1;
		}
		ans+=dp[v];
		dfs2(v,u);
		col[u]=pcu,dp[u]=pdpu,b[u]=pbu,w[u]=pwu,same[u]=psu;
		col[v]=pcv,dp[v]=pdpv,b[v]=pbv,w[v]=pwv,same[v]=psv;
		ans=pans;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	res=ans=0;
	for(int i=1;i<=m;++i){
		scanf("%d",&col[i]);
		if(col[i]==0)b[i]++;
		else w[i]++;
		ok[i]=1;
	}
	for(int i=1;i<n;++i){
		scanf("%d%d",&u,&v);
		e[u].pb(v);
		e[v].pb(u);
	}
	rt=m+1;
	dfs(rt,-1);
//	for(int i=1;i<=n;++i){
//		printf("i:%d b:%d w:%d same:%d col:%d dp:%d\n",i,b[i],w[i],same[i],col[i],dp[i]);
//	}
//	printf("ans:%d\n",ans);
	dfs2(rt,-1);
	printf("%d\n",m-res);
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值