zoukankan      html  css  js  c++  java
  • 【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

    小结:

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

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

  • 相关阅读:
    盒子垂直水平居中
    Sahi (2) —— https/SSL配置(102 Tutorial)
    Sahi (1) —— 快速入门(101 Tutorial)
    组织分析(1)——介绍
    Java Servlet (1) —— Filter过滤请求与响应
    CAS (8) —— Mac下配置CAS到JBoss EAP 6.4(6.x)的Standalone模式(服务端)
    JBoss Wildfly (1) —— 7.2.0.Final编译
    CAS (7) —— Mac下配置CAS 4.x的JPATicketRegistry(服务端)
    CAS (6) —— Nginx代理模式下浏览器访问CAS服务器网络顺序图详解
    CAS (5) —— Nginx代理模式下浏览器访问CAS服务器配置详解
  • 原文地址:https://www.cnblogs.com/Hansue/p/12104308.html
Copyright © 2011-2022 走看看