zoukankan      html  css  js  c++  java
  • CodeForces 294E Shaass the Great 树形DP

    原题直通车:CodeForces 294E Shaass the Great

    题意: 树中有n个点,从n-1条边中去除一条边,再构建一条相同长度的边重新构成一棵树

         (去除的边和构造的边可能相同),问新树中任意两点之间距离的总和最小是多少。

    分析:

          可通过枚举去除的边,考虑下面两点即可:

         Ⅰ、去除一条边之后一定会分成两棵树,构造新的边时,可知经过新边的次数=树1结点数*树2结点数。
         Ⅱ、树1中某个结点a到树2中结点时,必选到达树2的连接点,再通往其它点;

             且到所有点的距离总和=a到树1连接点+新边+树2连接点到所有点的距离和。

    代码: (参考神牛的思路写的)

    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const int maxn=6666;
    struct edge{
        int to;
        LL val;
        edge(int v,LL c):to(v),val(c){}
    };
    int n;
    int par[maxn];              // par[i]: i的父亲结点
    vector<edge>Tree[maxn];
    LL len_par[maxn];           // len_par[i]: i到父结点的距离
    LL so[maxn];                // so[i]: i树中结点数
    LL sp[maxn];                // sp[i]: i到i子树所有结点的距离和
    
    void dfs_par(int cnt){
        int len=Tree[cnt].size();
        for(int i=0;i<len;++i){
            int son=Tree[cnt][i].to;
            if(par[son]==son&&son!=0){
                par[son]=cnt;
                len_par[son]=Tree[cnt][i].val;
                dfs_par(son);
            }
        }
    }
    void dfs_sp(int cnt,LL &val,LL sum){
        val+=sum;  so[cnt]=1;
        int len=Tree[cnt].size();
        for(int i=0;i<len;++i){
            int son=Tree[cnt][i].to;
            if(par[son]==cnt){
                dfs_sp(son,val,sum+Tree[cnt][i].val);
                so[cnt]+=so[son];
            }
        }
    }
    void dfs_de(int cnt,int toal){
        int len=Tree[cnt].size();
        for(int i=0;i<len;++i){
            int son=Tree[cnt][i].to;
            if(par[son]==cnt){
                sp[son]=sp[cnt]+Tree[cnt][i].val*(toal-2*so[son]);
                //=sp[son]=sp[cnt]-Tree[cnt][i].val*so[son] + Tree[cnt][i].val*(toal-so[son]);
                dfs_de(son,toal);
            }
        }
    }
    void get_sp(int cnt,int &Min){
        if(sp[Min]>sp[cnt]) Min=cnt;
        int len=Tree[cnt].size();
        for(int i=0;i<len;++i){
            int son=Tree[cnt][i].to;
            if(par[son]==cnt) get_sp(son,Min);
        }
    }
    int main(){
        cin>>n;
        for(int i=1;i<n;++i){
            par[i]=i;
            int a,b; LL c;  cin>>a>>b>>c;
            --a, --b;
            Tree[a].push_back(edge(b,c));
            Tree[b].push_back(edge(a,c));
        }
        len_par[0]=par[0]=0;
        dfs_par(0);
        LL ans=-1;
        for(int i=1;i<n;++i){
            int r=par[i]; par[i]=i;
            dfs_sp(0,sp[0],0LL), dfs_sp(i,sp[i],0LL);
            dfs_de(0,so[0]), dfs_de(i,so[i]);
            int g1=0, g2=i;
            get_sp(0,g1), get_sp(i,g2);
            LL sp1=sp[g1], sp2=sp[g2];
            
    //        cout<<"--------"<<i<<"--------"<<endl;
    //        cout<<"g1="<<g1<<' '<<"g2="<<g2<<endl;
    //        for(int i=0;i<n;++i) cout<<sp[i]<<' '; cout<<endl;
            
            LL M=len_par[i]*so[0]*so[i]+sp1*so[i]+sp2*so[0];
            // M第一部分: 经过新建边的次数是 两树结点数的乘积
            // M第二部分: 一棵树中的每个结点都得经过另一棵树的连接点到那树上所有结点的路径
            LL p=0LL;
            for(int j=0;j<n;++j) p+=sp[j];  // 两棵树分别的结点两两距离
            M+=p/2;
            ans=(ans==-1?M:min(ans,M));
            memset(sp,0LL,sizeof(sp));
            par[i]=r;
        }
        cout<<ans<<endl;
        return 0;
    }
    



  • 相关阅读:
    Git fetch和git pull的区别
    git revert和git reset的区别
    JSF 与 HTML 标签的联系
    3. Decorator
    2. Observer
    1. Strategy
    继承构造函数的执行顺序
    模板特化
    8.1.2 Template instantiation (Accelerated C++)
    std::cin
  • 原文地址:https://www.cnblogs.com/bbsno1/p/3266683.html
Copyright © 2011-2022 走看看