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