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;
}