• 题目分享Q


    题意:给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大

    分析:这可以说是换根法的裸题吧

    首先考虑对一个给定的根如何计算,这应该是最简单的那种树形dp吧甚至可能都不算dp(好像还真不算dp)

    dp[i]表示i点所有孩子(包括自己)的深度之和

    deep[i]表示i点的深度

    dp[x]=sum(dp[v]) +deep[x]    v|v是x的子节点

    用一遍dfs即可搞定dp[1]-dp[n]

    当然这里用谁为根都可以,用1就行

     比如我们现在已经处理出dp[1]-dp[n]的值

    因为如果我们用2做根的时候,虽然发现所有人的dp值都改变了(每个人深度都改变了),这与我们之前学习的换根法可能会有一点区别

     但能发现的是,我们当前根节点的儿子想上位所需要改变的东西(dp值)其实O(1)就可以解决,或者说有规律可言,所以我们可以用换根法

    那么怎么改变?或者说有什么规律?

    首先要先记住一点的还是那个状态转移方程      dp[x]=sum(dp[v]) +deep[x]    v|v是x的子节点

    再考虑这里面中每一项都怎么变的

    就拿原来根是1换成2来说

    首先,1除了2以外的子树的所有节点包括1在内(1,3,6,7,8),深度都+1

    而2的子树包括2在内(2,3,5),深度都-1

    然后好像就没啥别的变化了

    那么再看dp值是怎么变的

    首先dp[1]的值要先减去dp[2]的值,得到1除了2以外的子树包括1在内的深度之和,而得到这个之和dp[1]还要加上这些节点的个数,因为每个节点的深度都加一嘛

    用son[i]表示i的子节点个数(包括自己)

    也就是说dp[1]=dp[1]-dp[2]+(son[1]-son[2])(这是1除了子树2以外的节点数目)

    而dp[2]是要先减去节点个数,再加上当前1的dp值(dp[1]修改后的值),因为此时1已经是2的儿子了

    dp[2]=dp[2]-son[2]+dp[1]

    注意这里是有先后顺序的

    当然我们在第一遍dfs的时候要多算一遍son[i]

    还有一点细节

    在我们换根的时候要记得dfs完这个子树要还原成原来的样子再dfs下一个子树,而我们在dfs子树的时候其实与根节点是没有关系的,dfs只能往下搜嘛

    也就是说我们在交换1和2的时候dp[1]的值不需要修改,所以我直接把dp[1]的式子带入到dp[2]的式子里就行了,也就是

    dp[2]=dp[2]-son[2]+dp[1]-dp[2]+son[1]=dp[1]+son[1]-son[2]*2

    最后把以所有点为根的结果取个最值就行了

    代码:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<vector>
     4 using namespace std;
     5 
     6 #define ll long long
     7 
     8 const int maxn=1e6+1;
     9 
    10 struct Node
    11 {
    12     int to,next;
    13 }e[maxn<<1];
    14 int head[maxn];
    15 ll dp[maxn];
    16 int son[maxn];
    17 int cnt,k,n;
    18 ll maxans;
    19 
    20 void add(int x,int y)
    21 {
    22     e[++cnt].to=y;
    23     e[cnt].next=head[x];
    24     head[x]=cnt;
    25 }
    26 
    27 void dfs1(int x,int fa,int now)
    28 {
    29     dp[x]=(ll)now,son[x]=1;
    30     for(int i=head[x];i;i=e[i].next)
    31     {
    32         int v=e[i].to;
    33         if(v!=fa)
    34         {
    35             dfs1(v,x,now+1);
    36             dp[x]+=dp[v];
    37             son[x]+=son[v];
    38         }
    39     }
    40 }
    41 
    42 void dfs2(int x,int fa)
    43 {
    44     if(dp[x]>maxans||dp[x]==maxans&&x<k) k=x,maxans=dp[x];
    45     for(int i=head[x];i;i=e[i].next)
    46     {
    47         int v=e[i].to;
    48         if(v!=fa)
    49         {
    50             ll now=dp[x]-dp[v]+(ll)son[x]-(ll)son[v]*2;//这里是为了方便还原,下面用的+=和-=
    51             int nowv=son[v];
    52             son[v]=son[x];
    53             dp[v]+=now;
    54             dfs2(v,x);
    55             dp[v]-=now;
    56             son[v]=nowv;
    57         }
    58     }
    59 }
    60 
    61 int main()
    62 {
    63     int n,x,y;
    64     scanf("%d",&n);
    65     for(int i=1;i<n;i++)
    66     {
    67         scanf("%d%d",&x,&y);
    68         add(x,y);add(y,x);
    69     }
    70     dfs1(1,-1,1);dfs2(1,-1);
    71     printf("%d",k);
    72     return 0;
    73 }
  • 相关阅读:
    浙江工业大学校赛 小马哥和数列
    浙江工业大学校赛 XiaoWei的战斗力
    浙江工业大学校赛 猜猜谁是我
    浙江工业大学校赛 竹之书(大数,同余定理)
    浙江工业大学校赛 画图游戏 BugZhu抽抽抽!!
    浙江工业大学校赛 画图游戏
    pta 天梯地图 (Dijkstra)
    Atom打造 c/c++编译环境(忙了一个上午)
    HRBUST
    CSU 1808 地铁 (Dijkstra)
  • 原文地址:https://www.cnblogs.com/lin4xu/p/12691031.html
Copyright © 2020-2023  润新知