• 【HNOI/AHOI2018】道路


    题目链接

    分析:

      这实际上就是给了一个二叉树,每个非叶子节点选一个儿子,然后对于每个叶子结点,它的贡献跟它的到根链上有几个节点作为左/右儿子没被选中有关,求最小的贡献和。

      考虑树形$DP$,然而这个贡献是个乘积,不方便直接拆分然后从下往上做套路的子树合并。

      观察数据范围:树的大小$4 imes 10^4$,深度$40$。发现对于每个叶子,可以枚举顶上的信息然后存下来;非叶子结点也是枚举顶上信息,再讨论两种情况。

      设$f[u][i][j]$表示:对于子树$u$,它的到根链上有$i$个没被选中的左二子、$j$个被选中的右儿子,子树的贡献和。那么$$f[u][i][j]=min{f[ls][i][j]+f[rs][i][j+1],f[ls][i+1][j]+f[rs][i][j]}$$

      题解和讨论区里好像说这题卡空间,那么我们可以根据$DFS$的性质,设$f[d][i][j][0/1]$表示第$d$层,左/右儿子的答案。顺利通过。

    实现(100分):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    #define IL inline
    #define RG register
    #define _1 first
    #define _2 second
    using namespace std;
    typedef long long LL;
    const int N=2e4;
    const int M=40;
    
        int n,cit[N+3],vil[N+3];
        
    struct Node{
        bool iscity;
        LL a,b,c;
        int s[2];
        
    }a[N*2+3];
        int top;
        
    IL int pickcity(int i){
        if(cit[i]==-1)
            return cit[i]=++top;
        return cit[i];
    }
    
        LL f[M+3][M+3][M+3][2];
    
    void dfs(int d,int t,int x,int rd,int ri){
        if(a[x].iscity){
            for(int i=0;i<2;i++)
                dfs(d+1,i,a[x].s[i],rd+(int)(i==0),ri+(int)(i==1));
            
            for(int i=0;i<=rd;i++)
                for(int j=0;j<=ri;j++)
                    f[d][i][j][t]=min(f[d+1][i][j][0]+f[d+1][i][j+1][1]
                                     ,f[d+1][i+1][j][0]+f[d+1][i][j][1]);
            
        }
        else {
            for(int i=0;i<=rd;i++)
                for(int j=0;j<=ri;j++)
                    f[d][i][j][t]=a[x].c*(a[x].a+i)*(a[x].b+j);
            
        }
        
    }
    
    int main(){
        scanf("%d",&n);
        top=-1;
        memset(cit,-1,sizeof cit);
        for(int i=1;i<n;i++){
            int k=pickcity(i);
            a[k].iscity=true;
            a[k].s[0]=a[k].s[1]=-1;
            
            int s[2],t;
            scanf("%d%d",&s[0],&s[1]);
            for(int j=0;j<2;j++)
            if(s[j]>0){
                t=pickcity(s[j]);
                a[t].iscity=true;
                a[k].s[j]=t;
                
            }
            else {
                t=++top;
                a[t].iscity=false;
                vil[-s[j]]=t;
                a[k].s[j]=t;
                
            }
            
        }
        
        for(int i=1;i<=n;i++)
            scanf("%lld%lld%lld",&a[vil[i]].a,&a[vil[i]].b,&a[vil[i]].c);
        
        dfs(1,0,0,0,0);
        
        printf("%lld",f[1][0][0][0]);
    
        return 0;
    
    }
    View Code

    小结:

      根据数据范围灵活选用方便的算法。

      写题的时候设节点花了点时间,有点手生了。

  • 相关阅读:
    luogu 2491 [SDOI2011]消防 / 1099 树网的核 单调队列 + 树上问题
    BZOJ 1179: [Apio2009]Atm tarjan + spfa
    BZOJ 1112: [POI2008]砖块Klo Splay + 性质分析
    BZOJ 1596: [Usaco2008 Jan]电话网络 树形DP
    BZOJ 2060: [Usaco2010 Nov]Visiting Cows 拜访奶牛 树形DP
    CF286E Ladies' Shop FFT
    CF528D Fuzzy Search FFT
    BZOJ 3771: Triple 生成函数 + FFT
    BZOJ 3513: [MUTC2013]idiots FFT
    python爬虫网页解析之parsel模块
  • 原文地址:https://www.cnblogs.com/Hansue/p/12104308.html
Copyright © 2020-2023  润新知