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

  

 

posted @ 2019-07-04 19:50  czh~  阅读(465)  评论(0编辑  收藏  举报