树形dp 各种奇怪的总结

1. 树的重心:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

性质:

  • 树中所有点到重心的距离和是最小的,一棵树最多有两个重心(比如就两个点连个边).
  • 把两棵树通过加一条边得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上.
  • 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置.

树的重心的两种求法: 

  • 按定义求:跑一遍dfs.
     1 #include <iostream>
     2 using namespace std;
     3 const int N = 2e5+10;
     4 int ans = 0x3f3f3f,h[N],e[N],ne[N],idx,st[N],n;//邻接表存储树
     5 void add(int a,int b)//不管是a插入b还是b插入a都是idx++,邻接表的idx应该是指边的编号
     6 {//e[idx] 是以idx为起点,该起点的值,ne[idx]是该边的终点 所以e存在值相同的点
     7     ne[idx] = h[a],h[a] = idx,e[idx++] = b;
     8 }
     9 int dfs(int u)//返回连通块大小
    10 {
    11     st[u] = 1;
    12     int sum = 1,res = 0;
    13     for(int i=h[u];i!=-1;i=ne[i])
    14     {
    15         int j = e[i];//e[i]存储的实际是该起点在邻接表数组的位置
    16         if(!st[j])
    17         {
    18            int s = dfs(j);
    19            res = max(res,s);//res是不算上u结点的子连通块最大的结点数
    20            sum+=s;
    21         }
    22     }
    23     ans = min(max(res,n-sum),ans);
    24     return sum;
    25 }
    26 int main()
    27 {
    28     fill(h,h+N,-1);
    29     scanf("%d",&n);
    30     for(int i=0;i<n-1;i++){
    31         int x,y; scanf("%d%d",&x,&y);
    32         add(x,y),add(y,x);
    33     }
    34     dfs(1);
    35     printf("%d\n",ans);
    36     return 0;
    37 }
    dfs

     

  • 树形dp:换根dp 枚举点,求其他点到该点的距离,取最小值.
     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4 typedef long long LL;
     5 const int N = 100010;
     6 int h[N],w[N],n,idx,m;
     7 LL f[N],sum[N];
     8 struct Road{
     9     int to,ne,w;
    10 }road[N<<1];
    11 void add(int a,int b,int c)
    12 {
    13     road[idx].w= c,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
    14 }
    15 void dfs_down(int u,int fa)
    16 {
    17     sum[u] = w[u]; 
    18     for(int i=h[u];i!=-1;i=road[i].ne)
    19     {
    20         int v = road[i].to;
    21         if(v==fa) continue;
    22         dfs_down(v,u);
    23         f[u] += f[v]+sum[v]*road[i].w;
    24         sum[u]+=sum[v];
    25     }
    26 }
    27 void dfs_up(int u,int fa)
    28 {
    29     for(int i=h[u];i!=-1;i=road[i].ne)
    30     {
    31         int v = road[i].to;
    32         if(v==fa) continue;
    33         f[v] = road[i].w*(m-2*sum[v])+f[u];
    34         dfs_up(v,u);
    35     }
    36 }
    37 int main() 
    38 {
    39     memset(h,-1,sizeof h);
    40     scanf("%d",&n);
    41     for(int i=1;i<=n;i++) scanf("%d",&w[i]),m+=w[i];
    42     for(int i=1;i<n;i++)
    43     {
    44         int a,b,c; scanf("%d%d%d",&a,&b,&c);
    45         add(a,b,c); add(b,a,c);
    46     }
    47     dfs_down(1,-1);
    48     dfs_up(1,-1);
    49     LL ans = 1e14;
    50     for(int i=1;i<=n;i++) ans = min(ans,f[i]);
    51     printf("%lld\n",ans);
    52     return 0;
    53 }
    换根dp

     

2. 树的中心:一个结点,对于每一个点的距离的最大值最小.

树的中心的求法:

  • 树形dp(树的直径):
     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4 const int N = 10010;
     5 int h[N],idx,n,d1[N],d2[N],by[N],up[N];
     6 struct Road{
     7     int fr,to,ne,w;
     8 }road[N<<1];
     9 void add(int a,int b,int c)
    10 {
    11     road[idx].w = c,road[idx].fr = a,road[idx].to = b,road[idx].ne =h[a],h[a]= idx++;
    12 }
    13 int dfs(int u,int fa)
    14 {
    15     for(int i=h[u];i!=-1;i=road[i].ne)
    16     {
    17         int v = road[i].to;
    18         if(v==fa) continue;
    19         int dist = dfs(v,u)+road[i].w;
    20         if(dist>=d1[u]) d2[u] = d1[u],d1[u] = dist,by[u] = v;
    21         else if(dist>d2[u]) d2[u] = dist;
    22     }
    23     return d1[u];
    24 }
    25 void dfs_up(int u,int fa)
    26 {
    27     for(int i=h[u];i!=-1;i=road[i].ne)
    28     {
    29         int v = road[i].to;
    30         if(v==fa) continue;
    31         if(by[u]==v) up[v] = max(d2[u],up[u])+road[i].w;
    32         else up[v] = max(d1[u],up[u])+road[i].w;
    33         dfs_up(v,u);
    34     }
    35 }
    36 int main()
    37 {
    38     scanf("%d",&n);
    39     memset(h,-1,sizeof h);
    40     for(int i=1;i<n;i++)
    41     {
    42         int a,b,c; scanf("%d%d%d",&a,&b,&c);
    43         add(a,b,c); add(b,a,c);
    44     }
    45     dfs(1,-1);
    46     dfs_up(1,-1);
    47     int ans = 0x3f3f3f3f;
    48     for(int i=1;i<=n;i++)
    49         ans = min(max(d1[i],up[i]),ans);
    50     printf("%d\n",ans);
    51     return 0;
    52 }
    换根dp
posted @ 2021-04-07 21:32  acmloser  阅读(62)  评论(0编辑  收藏  举报