换根dp 学习笔记

换根dp:树形dp的一类

例:P3478 [POI2008] STA-Station

题目大意:给定一个 \(n\) 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。

一个显然的 \(n^2\) 暴力:以每个点为根 分别跑一遍dfs暴力统计 也显然会 T 飞

我们可以先以1为根 dfs一遍求出答案 考虑如何从以1为根时的答案推出所有点的答案

然后我们可以发现 如果把1的儿子移上来 儿子子树内的所有点dep都 -1 而其它点dep++

(可以自己画一画体会一下)

可以得到转移:\(f_y=f_x-siz_y+(n-siz_y),y\in son_x\)

每次换根的时候 我们发现原子树内的其它点 \(siz\) 不变

那么再进行一次dfs 直接转移即可 时间复杂度 \(O(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=2e6+5;
const int M=8e2+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'|c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,u,v,siz[N],dep[N],f[N],ma,ans;
int head[N],nxt[N],to[N],cnt;
inl void add(int u,int v){
    nxt[++cnt]=head[u];
    to[cnt]=v;
    head[u]=cnt;
}
inl void dfs1(int x,int fa){
    siz[x]=1;dep[x]=dep[fa]+1;f[1]+=dep[x];
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
    }
}
inl void dfs2(int x,int fa){
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa)continue;
        f[y]=f[x]+n-(siz[y]<<1);
        dfs2(y,x);
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n-1;i++){
        u=read();v=read();
        add(u,v);add(v,u);
    }
    dep[0]=-1;dfs1(1,0);dfs2(1,0);
    for(int i=1;i<=n;i++)
        if(f[i]>ma)ans=i,ma=f[i];
    writel(ans);
    return 0;
}

ybtoj 树形dp I.周年纪念日 \(/\) P2986 [USACO10MAR] Great Cow Gathering G

(ybt得先求个最小生成树 其他都一样)

其实和上一道题差不多 每次换根每条边的贡献从1变成了边权 子树大小由点数变点权和 其他都一样

记得开long long ( long long max是 \(0x7 + 15\) 个 $f\ $ 少打一个 \(f\) 调好久()

其它点减少数量是 点权和 $-siz_y\ $ 不是 \(n-siz_y\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=2e6+5;
const int M=8e2+5;
const int inf=0x7fffffffffffffff;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'|c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,u,v,c,siz[N],f[N],p[N],mi=inf,ans,tot;
int head[N],nxt[N],to[N],w[N],cnt;
inl void add(int u,int v,int c){
    nxt[++cnt]=head[u];
    to[cnt]=v;w[cnt]=c;
    head[u]=cnt;
}
inl void dfs1(int x,int fa){
    siz[x]=p[x];
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(y==fa)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        f[1]+=siz[y]*c;
    }
}
inl void dfs2(int x,int fa){
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(y==fa)continue;
        f[y]=f[x]+(tot-(siz[y]<<1))*c;
        dfs2(y,x);
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++)p[i]=read(),tot+=p[i];
    for(int i=1;i<=n-1;i++){
        u=read();v=read();c=read();
        add(u,v,c);add(v,u,c);
    }
    dfs1(1,0);dfs2(1,0);
    for(int i=1;i<=n;i++)
        if(f[i]<mi)ans=i,mi=f[i];
    writel(mi);
    return 0;
}
posted @ 2023-10-10 10:47  xiang_xiang  阅读(11)  评论(0)    收藏  举报