cf1187E-Tree Painting(换根dp)

13 篇文章 2 订阅
8 篇文章 1 订阅

传送门

题意

给定一棵具有 n n n 个结点,且初始状态全为白色的树,最初可选择任意一个点将其染为黑色,之后的操作只能选择某个黑色点相邻的白色点进行染色。每次操作之后,将得到包含该顶点的白色连通块中结点个数的贡献值,求染黑所有结点后能够得到的最大贡献值是多少。

发现最后的答案值只与起手时选择染黑的第一个点有关,考虑将第一个被染色的点看作根,那么染色过程就是从根节点开始进行 d f s dfs dfs 操作。而每次得到的贡献值其实就是以该节点为根的子树的结点数,由此转化为计数换根 d p dp dp 题(这不是显而易见的吗)。

为了便于操作,接下来所有描述将把结点 1 1 1 作为初始时的根节点。那么对于样例 2 2 2 有以下(见图):

在这里插入图片描述

c n t cnt cnt 数组记录以某个结点作为根的子树中所有结点数量,则图中表格的意思是:当以 i i i 作为根节点时,编号为 j j j 的点的 c n t cnt cnt 。发现当把某儿子结点提上来作为新的根时,表格中只有原来的根新的根 c n t cnt cnt 值将改变。

因此若此时的根为结点 1 1 1 ,且下一时刻将把结点 2 2 2 提为根,则只有 c n t 1 cnt_1 cnt1 c n t 2 cnt_2 cnt2 会发生改变。并且, c n t 1 cnt_1 cnt1 原先为 n n n(所有结点的个数), c n t 2 cnt_2 cnt2 将改变为 n n n ,则真正需要关心的是 c n t 1 cnt_1 cnt1 将要改变的值和 c n t 2 cnt_2 cnt2 原先的值

其中, c n t 2 cnt_2 cnt2 的值是在跑完整棵树的 c n t cnt cnt 时已知的,而 c n t 1 cnt_1 cnt1 的值即为 n − c n t 2 n-cnt_2 ncnt2 ,可以得到结论:当目前是 1 结点为根,且下一步将把 2 结点作为新的根时,对答案的贡献是: a n s − c n t 2 + n − c n t 2 ans-cnt_2+n-cnt_2 anscnt2+ncnt2 ,将两个对应的 c n t cnt cnt 进行修改后继续跑 d f s dfs dfs 即可。注意还原现场!!

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+10;
vector<int> h[N];
int cnt[N];
int maxx=0,n;

void deal(int u,int fa){
	cnt[u]=1;
	for(auto x:h[u]){
		if(x==fa)
			continue;
		deal(x,u);
		cnt[u]+=cnt[x];
	}
}

void dfs(int u,int fa,int ans){
	//cout<<u<<" "<<ans<<endl;
	for(auto x:h[u]){
		if(x==fa)
			continue;
		int temp1=cnt[u],temp2=cnt[x];
		cnt[u]=n-cnt[x],cnt[x]=n;   
		maxx=max(maxx,ans-temp2+cnt[u]);
		dfs(x,u,ans-temp2+cnt[u]);//当前根的原贡献 之前根现在贡献  
		cnt[u]=temp1,cnt[x]=temp2;
		//还原现场 
	}
}

signed main(){
	int u,v;
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>u>>v;
		h[u].push_back(v);
		h[v].push_back(u);
	}
	
	deal(1,0);
	for(int i=1;i<=n;i++){
		maxx+=cnt[i];//以1为根节点时的答案
	}
	dfs(1,0,maxx); 
	cout<<maxx<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值