题目链接http://codeforces.com/contest/1187/problem/E
题意
给出一棵树,树上每个节点初始都是白色。
一开始任意选择一个点染成黑色,然后每次选择一个与黑点相邻的白点染色。
每次染色会获得一个值,这个值为包含所选点的白色连通块的大小。
题解
假设第一个点确定,那就是个树形dp,dp方程是:
d
p
[
u
]
=
∑
d
p
[
v
]
+
s
i
z
e
[
u
]
dp[u]=\sum{dp[v]}+size[u]
dp[u]=∑dp[v]+size[u]其中v是u这颗子树下与u相邻的节点。
一开始我的思路是求出所有子树(无论方向)的size,理论上记忆优化的去做复杂度是O(n)的。但是可能是写的丑了,碰到个菊花图复杂度退化成n^2。
但如果仔细观察就会发现,如果u和v相邻,以u为根的值与以v为根的值相差的仅为size[u]和size[v]的变化,所以只要最开始dfs一个答案,在此基础上再dfs更换根节点,更新一遍答案就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+7;
ll n;
struct Edge{
ll v,nxt;
}e[N*2];
ll p[N],edn;
void add(ll u,ll v){
e[++edn]=(Edge){v,p[u]};p[u]=edn;
e[++edn]=(Edge){u,p[v]};p[v]=edn;
}
ll sz[N],ans,tmp;
void dfs1(ll u,ll f){
sz[u]=1;
for(ll i=p[u];~i;i=e[i].nxt){
ll v=e[i].v;
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
}
ans+=sz[u];
}
void dfs2(ll u,ll f){
for(ll i=p[u];~i;i=e[i].nxt){
ll v=e[i].v;
if(v==f) continue;
ll szu=sz[u],szv=sz[v],tt=tmp;
tmp-=sz[u]+sz[v];
sz[u]=n-sz[v];
sz[v]=n;
tmp+=sz[u]+sz[v];
ans=max(ans,tmp);
dfs2(v,u);
sz[u]=szu,sz[v]=sz[v],tmp=tt;
}
}
int main(){
scanf("%lld",&n);
memset(p,-1,sizeof(p));edn=-1;
for(ll i=1,u,v;i<n;i++){
scanf("%lld%lld",&u,&v);
add(u,v);
}
dfs1(1,0);
tmp=ans;
dfs2(1,0);
printf("%lld\n",ans);
}