题意:
在一棵n个节点的树上涂色,一开始所有的点都是白的,第一次可以任意选择一个白点涂成黑色。之后的每一次都要选择一个与黑点相邻。答案的构成:每次选择白色点的时候,答案要加上所有和这个白点连通的白点数量(包括自己)(白-黑-白的情况,两个白点不连通)
思路:
如果以一个点为根,一开始涂黑这个点,然后向下拓展,每次选择白点要记录的答案就是该点下面的子树的大小。然后采用“换根”的思路。在换根的时候分析一下整个答案的变化,当前节点为u,下面要转移的节点是v。ans[u]表示u作为树根的答案,那么当根变为v的时候,以u为根的子树就要作为v的一棵子树,这样一来,新的答案中,原以v为根的子树的大小多算了,而新的以u为根的子树大小还没有算入,所以状态转移:ans[v]=(ans[u]-sz[v])+(n-sz[v])
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n;
int sz[N];
int head[N],cnt;
struct node{
int v,nxt;
}edge[N<<1];
void addedge(int u,int v){
edge[++cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
ll ans=0;
void dfs1(int u,int fa){
sz[u]=1;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
dfs1(v,u);
sz[u]+=sz[v];
}
ans+=1ll*sz[u];
}
void dfs2(int u,int fa,ll maxn){
ans=max(ans,maxn);
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
dfs2(v,u,maxn-sz[v]+1ll*n-sz[v]);
}
}
int main(){
scanf("%d",&n);
memset(head,-1,sizeof(head));
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1,0);
dfs2(1,0,ans);
printf("%lld\n",ans);
return 0;
}