zoukankan      html  css  js  c++  java
  • 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
Copyright © 2011-2022 走看看