codeforces#1187E. Tree Painting(树换根)
题目链接:
http://codeforces.com/contest/1187/problem/E
题意:
给出一颗树,找到一个根节点,使所有节点的子节点数之和最大
数据范围:
$2 \le n \le 2 \cdot 10^5$
分析:
最暴力的方法是枚举所有的根节点,计算他们子节点之和,复杂度是$O(n^2)$
但是可以发现,$a$作为根节点和$b$作为根节点,只有$a,b$节点的子节点数有变化
根的子节点数肯定是$n$,与他交换的点节点数是$x$的话,那么根的子节点数就变成$n-x$,交换点就变成$n$,前提是交换点与根是相邻的
1.先以$1$作为根节点,计算出每个节点的子节点数,并且算出$1$节点第一个涂色的答案
2.把根节点变成与$1$相邻的点,计算答案,取最大值
3.往后递归
ac代码:
#include<bits/stdc++.h> #define ll long long #define pa pair<int,int> using namespace std; const int maxn=2e5+10; const ll mod=1e9+7; vector<int>ve[maxn]; int num[maxn],n; ll sum,ans; bool vis[maxn]; int dfs1(int x) { num[x]=1; for(int i=0;i<ve[x].size();i++) { int v=ve[x][i]; if(vis[v]==0) { vis[v]=1; num[x]+=dfs1(v); } } sum+=num[x]; return num[x]; } void dfs2(int x,int f) { sum+=(n-2*num[x]); num[f]=n-num[x]; num[x]=n; ans=max(sum,ans); for(int i=0;i<ve[x].size();i++) { int v=ve[x][i]; if(vis[v]==0) { vis[v]=1; dfs2(v,x); } } sum+=(n-2*num[f]); num[x]=n-num[f]; num[f]=n; } int main() { scanf("%d",&n); for(int i=1;i<=n-1;i++) { int a,b; scanf("%d %d",&a,&b); ve[a].push_back(b); ve[b].push_back(a); } vis[1]=1; dfs1(1); ans=sum; memset(vis,0,sizeof(vis)); vis[1]=1; for(int i=0;i<ve[1].size();i++) { int v=ve[1][i]; vis[v]=1; dfs2(v,1); } printf("%lld\n",ans); return 0; }