HDU5834 Magic boy Bi Luo with his excited tree(换根dp)

这道题算法很好想,为了不枚举每一个点做一遍,因此考虑换根dp,也就是先正的做一遍,之后从上面来更新下面的节点,难点在于细节非常复杂。

首先我们可以想到维护的是f[i][],g[i][],表示子树和上方的信息

分为f[i][0],和f[i][1]表示取完后回到i以及在i的某个子树中停留。

剩下的更新方程十分复杂,我在代码旁边有着注释,便于理解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=5e5+10;
const int inf=0x3f3f3f3f;
const ll mod=998244353;
int a[N],n;
int h[N],ne[N],e[N],w[N],idx;
int f[N][2],g[N][2];
int id[N];
void add(int a,int b,int c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dfs(int u,int fa){
    f[u][0]=f[u][1]=a[u];
    int i;
    for(i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(j==fa)
            continue;
        dfs(j,u);
        f[u][1]+=max(0,f[j][0]-2*w[i]);//含义是停留的地方不变,因此当前的子树如果大于0需要走
        if(f[u][0]+f[j][1]-w[i]>f[u][1]){//更改停留地点为当前子树上的点,如果更优,则更换
            f[u][1]=f[u][0]+f[j][1]-w[i];
            id[u]=j;
        }
        f[u][0]+=max(0,f[j][0]-2*w[i]);
    }
}
void get(int u,int fa){
    if(f[u][0]+g[u][1]>f[u][1]+g[u][0]){
        id[u]=fa;
    }
    int i;
    for(i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(j==fa)
            continue;
        if(j==id[u]){
            int tmp1=g[u][1]+a[u],tmp2=g[u][0]+a[u];//tmp1目前是父亲往上的停留点,tmp2目前是回到父亲的点
            for(int k=h[u];k!=-1;k=ne[k]){
                int v=e[k];
                if(v==j||v==fa)
                    continue;
                tmp1+=max(0,f[v][0]-2*w[k]);
                if(tmp2+f[v][1]-w[k]>tmp1){
                    tmp1=tmp2+max(0,f[v][1]-w[k]);
                }
                tmp2+=max(0,f[v][0]-2*w[k]);
            }
            g[j][0]=max(0,tmp2-2*w[i]);//回到当前点的最大值
            g[j][1]=max(0,tmp1-w[i]);//停留在上面的最大值
        }
        else{
            int tmp;
            //tmp是因为我们做的是上方的信息,因此要取消这个子树的信息
            if(f[j][0]>=2*w[i]){
                tmp=f[j][0]-2*w[i];
            }
            else{
                tmp=0;
            }
            g[j][0]=max(0,f[u][0]+g[u][0]-tmp-2*w[i]);
            g[j][1]=max(0,max(g[u][1]+f[u][0]-tmp-w[i],f[u][1]+g[u][0]-tmp-w[i]));
        }
        get(j,u);
    }
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    int cnt=1;
    while(t--){
        cin>>n;
        int i;
        idx=0;
        for(i=0;i<=n;i++){
            h[i]=-1;
            f[i][1]=f[i][0]=g[i][1]=g[i][0]=0;
            id[i]=-1;
        }
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<n;i++){
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
            add(b,a,c);
        }
        dfs(1,-1);
        get(1,-1);
        cout<<"Case #"<<cnt++<<":"<<endl;
        for(i=1;i<=n;i++){
            cout<<(max(f[i][0]+g[i][1],f[i][1]+g[i][0]))<<endl;
        }
    }
    return 0;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13591889.html