codeforce E. Tree Painting(树形dp||换根dp)

https://codeforces.com/contest/1187/problem/E

题意:给出一棵树,一开始每个节点都为白点,每次可以选择一个点变为黑点,获得分数等于该白点所有相连白点的数量,问能得到的最大分数为多少

思路:此题为经点的树形换根dp。
换根dp大体思路:
1.首先固定一个节点(一般取根节点)
2.考虑以该节点计算答案需要知道哪些条件(一般为子树的信息)
3.将根节点传递给子树,本节点以及子节点需要更改的信息
4.子节点交换完毕,将该点信息以改变相反顺序还原

考虑这道题:
1.首先固定0节点,以0节点获得的最优答案显然是依次选择每个相邻节点直到叶子节点
2.思考需要的条件:以每个子节点为根的所有子树的节点和+本身子节点数目,(每次选择一个点需要计算本身节点的和及子节点子树和),所以在第一次dfs处理处每个子节点数目num,以及子节点为根的子树节点和sum,那么0节点的答案就是num[0]
3.考虑换根 x->to 断开x-to连接 ,将to作为新的根节点,对应num,sum变化(具体解释看代码)
4.该子节点换根完毕,恢复num,sum

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define fi first
#define se second
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
using namespace std;

typedef long long ll;
typedef pair<char, ll> P;
typedef pair<P, int> LP;
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 100;
const int mod = 1e9+7;
const int base=131;
tr1::unordered_map<ll,ll> mp;
inline ll mul(ll x,ll y) { return (x*y-(ll)((long double)x*y/mod)*mod+mod)%mod;}
inline ll ksm(ll a,ll b) {ll ans=1;while(b){if(b&1)ans=mul(ans,a);a=mul(a,a),b>>=1;}return ans;}


ll n,m;
ll num[N],vis[N],a[N],sum[N];
ll k,ans,cnt,res,x,y;
vector<int> v[N];

void dfs(int x,int fa)
{
	num[x]=1;
	for(int to:v[x])
	{
		if(to==fa) continue;
		dfs(to,x);
		sum[x]+=sum[to];//所有子节点为根的子树大小的和
		num[x]+=num[to];//处理每个节点子树大小
	}
	sum[x]+=num[x];
}
void go(int x,int fa)
{
	ans=max(ans,sum[x]);
	for(int to:v[x])
	{
		if(to==fa) continue;

		sum[x]-=sum[to];//新树以to作为根,那么x减去to作为子节点的数目
		sum[x]-=num[to];// 这里原本sun[x]=sum[to]+num[x] 所以要减去这两个
		num[x]-=num[to];//减去该子节点子树大小
		sum[to]+=sum[x];//to作为根+x的子树和
		sum[to]+=num[x];//同理加上子树大小
		num[to]+=num[x];//加上x作为子树的大小


		go(to,x);//遍历子节点

		num[to]-=num[x];
		sum[to]-=num[x];
		sum[to]-=sum[x];
		num[x]+=num[to];
		sum[x]+=num[to];
		sum[x]+=sum[to];
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin>>n;
	for(int i=1;i<n;i++)
	{
		cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs(1,-1);
	go(1,-1);
	cout<<ans<<endl;


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值