题意:给你一棵无根树,每次你可以选择一个点从白点变成黑点(除第一个点外别的点都要和黑点相邻),变成黑点后可以获得一个权值(白点组成连通块的大小) 问怎么使权值最大
思路:首先,一但根确定了,整棵树的权值就只需要模拟即可,所以思路就转换为求哪一个点为根的权值最大。
这题需要用到一个二次扫描换根的思想,我们可以先从任意一个点去进行树形dp 并且得到从这个点开始去逐渐更新他的儿子节点
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define ll long long int using namespace std; inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;} int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]={1,0 ,0,1 ,-1,0 ,0,-1}; int dirs[8][2]={1,0 ,0,1 ,-1,0 ,0,-1, -1,-1 ,-1,1 ,1,-1 ,1,1}; const int inf=0x3f3f3f3f; const ll mod=1e9+7; int n; vector<int> G[200007]; ll dp[200007]; //表示i节点以下的所有贡献 ll f[200007]; //以i为根的权值 ll nump[200007]; //儿子节点数(包含自己) void dfs(int u,int fa){ nump[u]=1; for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(v==fa) continue; dfs(v,u); nump[u]+=nump[v]; } } void dfss(int u,int fa){ for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(v==fa) continue; dfss(v,u); dp[u]+=dp[v]; } dp[u]+=nump[u]; } void change(int u,int fa){ for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(v==fa) continue; f[v]=f[u]-nump[v]-dp[v]+n-nump[v]+dp[v]; //核心代码 change(v,u); } } int main(){ ios::sync_with_stdio(false); cin>>n; for(int i=1;i<n;i++){ int u,v; cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } dfs(1,0); dfss(1,0); f[1]=dp[1]; change(1,0); ll ans=0; for(int i=1;i<=n;i++){ ans=max(ans,f[i]); } cout<<ans<<" "; return 0; }