7.28 Day2 Problem 2

问题 B: Problem 2
时间限制: 2 Sec 内存限制: 256 MB
提交: 179 解决: 13
[提交][状态]
题目描述
你在一棵由n个点组成的树(一个无向连通的无环图)上玩游戏。

起初,所有点都是白色的。游戏的第一步,你选择一个点并把它染成黑色。然后,接下来的每一步中,选择一个与某个黑色点相邻的白色点,并将其染为黑色。

每次选择点时,都会获得与 所选点联通的白色点的数目(包括所选点) 相等的分数。游戏结束时,所有的点都被染成黑色。

下面是一个示例:

点1和4已经被染黑。如果你选择点2,你会获得4分(联通的白色点有2,3,5,6).如果你选择点9,你会获得3分(联通的白色点有7,8,9)。

你的任务是使得分最大。

Input

第一行包含一个整数n(2≤n≤2×105)代表树包含的点数。

接下来n-1行每行描述了树的一条边。第i行两个整数ui和vi代表ui和vi之间有边相连

(1≤ui,vi≤n,ui≠vi)。

Output

输出一行一个整数,你能得到的最大得分。

Example

input

9
1 2
2 3
2 5
2 6
1 4
4 9
9 7
9 8
output

36

input

5
1 2
1 3
2 4
2 5
output

14

Note

第一个例子中,树的形态如题目描述所示。

Hint

对于30%的数据,n≤2,000

对于另外20%的数据,保证原图是一条链
林肯是大头:http://www.accoders.com/problem.php?cid=1985&pid=1
思路:
1.一道树上换根问题
首先发现答案就是以一个点为根的其所有子树的根的儿子个数之和
进一步考虑,需要枚举每一个点进行比较,时间复杂度为O(N*(N+M))
所以考虑进行换根
一边深搜后得到以1为根的答案,存到一个数组ans中
接着进行下一次深搜,每一个非原根的点的答案由其父亲转移而来
ans[x]=ans[fa]+n-2*siz[x];
-siz[x]:
实际上就是去掉以其父亲为根时以x为根的这棵子树的儿子数(以x为根时已消失,因为x做根时不再是任何一个点的儿子)
n-siz[x]
再加上以fa为根的除x外另一棵树的儿子数,即所有的点减去以x为根的这棵子树的儿子数
2.另一种想法,每一条边(单向)经过该边时带来的贡献是一定的
(因为如果从相反方向过的话就走另一条边了。。。)
很巧妙的想法

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2*100000+10;
int n;
struct Edge
{
    int to,next;
}edge[maxn*2];
int first[maxn];
int cnt=0;
void add(int x,int y)
{
    cnt++;
    edge[cnt].next=first[x];
    edge[cnt].to=y;
    first[x]=cnt;
}
 
long long maxx=0;
long long ans[maxn];
long long siz[maxn];
void dfs1(int x,int fa)//算出每个点以1为根时儿子数及以1为根答案
{
    siz[x]=1;
    for(int i=first[x];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dfs1(v,x);
        siz[x]+=siz[v];
    }
    ans[1]+=siz[x];
}
void dfs2(int x,int fa)//换根搜索
{
    if(!ans[x])ans[x]=ans[fa]+n-siz[x]*2;
    //printf("x%d %d\n",x,ans[x]);
    maxx=max(ans[x],maxx);
    for(int i=first[x];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dfs2(v,x);
    }
}
int main()
{
    scanf("%d",&n);
    //printf("%d",n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
/*  for(int i=1;i<=n;i++)
    printf("%d ",in[i]);
    printf("\n");*/
    dfs1(1,0);
    //printf("1 %d\n",ans[1]);
    dfs2(1,0);
    printf("%lld",maxx);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值