换根dp

典题:

题目链接:https://www.luogu.com.cn/problem/P2986

题意:

给定一棵树(有边权且有点权),选取一个节点作为根节点,使得 其他节点到根节点的距离 x 点权 之和最小

思路:

换根dp,从根节点为1开始进行状态转移

观察可得,根节点每往相邻子节点移动一格,相邻子节点方向的 答案贡献减少,而父节点方向的 答案贡献增加

具体来说,转移后的节点答案相较于 父节点答案 减少了 相邻子节点方向的权值和x转移边边长, 增加了 父节点方向的权值和x转移边边长

一次dfs容易得到根节点为1时的答案,同时得到了每个子树的权值和

观察题目没有考察容斥

int c[maxn];
vector<pii>e[maxn];
int res[maxn];
int siz[maxn];
int ans;
int n;
int sum;
void dfs(int u,int fa,int k){
    siz[u]+=c[u];
    ans+=k*c[u];
    for(pii x:e[u]){
        int v=x.fi,len=x.se;
        if(v==fa)continue;
        dfs(v,u,k+len);
        siz[u]+=siz[v];
    }
}
void dfs2(int u,int fa,int k){
    if(u!=1){
        res[u]=res[fa]-siz[u]*k+(sum-siz[u])*k;
    }
    for(pii x:e[u]){
        int v=x.fi,len=x.se;
        if(v==fa)continue;
        // res[v]=res[u]-siz[v]*len+(sum-siz[v])*len;
        dfs2(v,u,len);
    }
}

void solve(){
    cin>>n;
    rep(i,1,n){
        cin>>c[i];
        sum+=c[i];
    }
    rep(i,1,n-1){
        int u,v,l;cin>>u>>v>>l;
        e[u].pb({v,l});
        e[v].pb({u,l});
    }
    dfs(1,0,0);
    res[1]=ans;
    dfs2(1,0,0);
    int temp=llmax;
    for(int i=1;i<=n;i++){
        // debug(res[i]);
        temp=min(temp,res[i]);
    }
    cout<<temp<<endl;
}

题目链接:https://www.luogu.com.cn/problem/P3047

题意:

求树上每一个节点与其距离不超过k的所有节点的权值和

思路:

记f[i][j]:与i节点距离小于等于j的节点权值和(子树方向)

d[i][j]:与i节点距离小于等于j的节点权值和(子树方向+父节点方向)

那么答案为d[i][k]

一次dfs易求得f[i][j],转移方程:f[u][j]+=f[v][j-1]

注意f[u][j]需要累加u节点自身的权值

第二次dfs,转移方程:d[u][j] = d[fa][j-1]-f[u][j-2] + f[u][j]
当j为1时d[u][j]=f[u][j]+f[fa][0]

当u节点作为根节点时,与它距离小于等于j的权值和 = u子树方向的权值和 + u父节点方向的权值和

其中u子树方向的权值和容易得到为f[u][j]

容斥:u父节点的方向权值= u父节点作为根节点,与它距离小于等于j-1的权值和 - u子树方向与u距离小于等于j-2的权值和

由于根节点的转移才导致-1和-2

vector<int>e[maxn];
int c[maxn];
int f[maxn][25];
int n,k;
int d[maxn][25];
void dfs(int u,int fa){
    for(int i=0;i<=k;i++){
        f[u][i]+=c[u];
    }
    for(int v:e[u]){
        if(v==fa)continue;
        dfs(v,u);
        for(int j=1;j<=k;j++){
            f[u][j]+=f[v][j-1];
        }
    }
}
void dfs2(int u,int fa){
    if(u!=1){
        d[u][1]+=f[fa][0];
        for(int j=2;j<=k;j++){
            d[u][j]+=d[fa][j-1]-f[u][j-2];
        }
    }
    for(int v:e[u]){
        if(v==fa)continue;
        dfs2(v,u);
    }
}
void solve(){
    cin>>n>>k;
    rep(i,1,n-1){
        int u,v;cin>>u>>v;
        e[u].pb(v);e[v].pb(u);
    }
    rep(i,1,n){
        cin>>c[i];
    }
    dfs(1,0);
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++){
            d[i][j]=f[i][j];
        }
    }
    dfs2(1,0);
    for(int i=1;i<=n;i++){
        cout<<d[i][k]<<endl;
    }
}
posted @ 2025-05-15 18:22  Marinaco  阅读(20)  评论(0)    收藏  举报
//雪花飘落效果