zoukankan      html  css  js  c++  java
  • [BZOJ4446]SCoi2015 小凸玩密室 树形DP(烧脑高能预警)

    4446: [Scoi2015]小凸玩密室

    Time Limit: 10 Sec  Memory Limit: 128 MB

    Description

    小凸和小方相约玩密室逃脱,这个密室是一棵有n个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯
    泡即可逃出密室。每个灯泡有个权值Ai,每条边也有个权值bi。点亮第1个灯泡不需要花费,之后每点亮4
    个(1个)新的灯泡V的花费,等于上一个被点亮的灯泡U到这个点V的距离Du,v,乘以这个点的权值Av。在点灯
    的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。

    Input

    第1行包含1个数n,代表节点的个数
    第2行包含n个数,代表每个节点的权值ai。(i=l,2,…,n)
    第3行包含n-l个数,代表每条边的权值bi,第i号边是由第(i+1)/2号点连向第i+l号点的边。
    (i=l,2...N-1)

    Output

    输出包含1个数,代表最少的花费。

    Sample Input

    3
    5 1 2
    2 1

    Sample Output

    5

    HINT

    对于100%的数据,1≤N≤2×105,1<Ai,Bi≤10^5


    (其实我是回来补暑假没写完的题解的)

    题解:

    (首先我经过激烈的思想斗争,认为那个点亮灯泡的花费计算只与上一个有关(其实是因为前4个太难考虑了...)

    事实证明这样做是对的..就是只考虑上一个:(

    有了之前做非线性DP的经验,我一开始想的还是合并类型

    但是发现数据范围不太对....这似乎是一个介于O(n)和O(n2)范围内的DP

    我们考虑怎么定义状态,以及怎么转移.

    一开始我想的是f[i][j]表示"走完以i为根的子树之后走去j点的最小花费"

    但是我发现这个MLE了,2e5开不下

    但是这又是一颗完全二叉树,所以我们考虑能不能应用他的一些性质

    我们观察到,点灯泡的起点没有确定,但是题目有这样的两个限制:

    要保证任意时刻所有被点亮的灯泡必须连通.

    在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡

    这样的话,某一个点被点亮的时候只能有三种转移的情况:从儿子走来,从兄弟走来和从父亲走来(废话)

    因此,当某一个子树被完全点亮之后,我们就要去某一个他的祖先,或者是他某个祖先的儿子

    我们发现上面这两个都与这个节点的祖先有关(这里我们把自己也看成自己的祖先)

    由于这是一棵二叉树,我们完全可以通过位运算(左移,右移和异或)来计算出某个节点的某个深度的祖先.

    这样,第二维完全不用是O(n)的:我们可以把第二维设为深度,即走完以i为根的子树之后走去深度为j的祖先节点的“XXXX”的最小花费

    那么我们再考虑一下:我们这个点的祖先节点可能已经被点亮,也可能没有被点亮。如果已经点亮,我们就必须去点亮祖先的兄弟那棵子树。

    因此我们定义两个数组:

    f(ather)[i][j]表示走完以i为根的子树之后去点亮深度为j的祖先节点的最小花费

    b(rother)[i][j]表示走完以i为根的子树之后去点亮深度为j的祖先节点的兄弟节点的最小花费

    接下来我们考虑状态的转移。最好想到的是节点i是叶子节点的情况:直接走去对应的节点即可。

    我们再考虑下一种情况:只有一个儿子。那么此时我们必须先走到这个儿子,然后再从这个儿子走去对应的节点。
    如果有2个儿子,我们应该考虑两种情况,即先去左儿子还是先去右儿子,然后取较小值,并累加中间的权值。
    这样,转移部分就被我们解决了。具体代码实现就是上面这个思路的体现。
    最后,我们再来考虑“第一个灯泡不确定”这个条件
    假设第一步点亮了灯泡u,设它的兄弟为bro(ther),那么接下来我们应该一步一步往上爬,直到点亮所有灯泡。
    也就是说重复u->fa->bro->fa->bro->......这个过程
    我们只需要模拟这个过程,就可以得到第一步点亮u的最小花费(记得讨论bro不存在的情况)
    然后在所有最小值里取最小即可。
    这道题的思维含量很高,是一道不可多得的好题呀!

    完整代码见下:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N=200010;
     6 typedef long long LL;
     7 int n,l[N],r[N],deep[N];
     8 LL f[N][19],b[N][19],a[N],lval[N],rval[N],dis[N];
     9 int main()
    10 {
    11     scanf("%d",&n);LL v;int rt;deep[1]=1;
    12     for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    13     for(int i=1;i<n;i++)
    14     {
    15         scanf("%lld",&v);
    16         rt=(i+1)>>1,deep[i+1]=deep[rt]+1;
    17         if((i+1)&1) r[rt]=i+1,rval[rt]=v,dis[r[rt]]=dis[rt]+rval[rt];
    18         else l[rt]=i+1,lval[rt]=v,dis[l[rt]]=dis[rt]+lval[rt];
    19     }
    20     for(int i=n;i>1;i--)
    21         for(int j=2;j<=deep[i];j++)
    22             if(!r[i])
    23                 if(!l[i])
    24                 {
    25                     int fa=i>>(deep[i]-j+1),fab=(i>>(deep[i]-j))^1;
    26                     b[i][j]=( dis[i]+dis[fab]-(dis[fa]<<1) )*a[fab];
    27                 }
    28                 else b[i][j]=lval[i]*a[l[i]]+b[l[i]][j];
    29             else b[i][j]=min(lval[i]*a[l[i]]+b[l[i]][deep[i]+1]+b[r[i]][j],rval[i]*a[r[i]]+b[r[i]][deep[i]+1]+b[l[i]][j]);
    30     for(int i=n;i;i--)
    31         for(int j=0;j<=deep[i];j++)
    32         {
    33             if(!r[i])
    34                 if(!l[i])
    35                     if(!j)f[i][j]=0;
    36                     else
    37                     {
    38                         int fa=i>>(deep[i]-j);
    39                         f[i][j]=(dis[i]-dis[fa])*a[fa];
    40                     }
    41                 else f[i][j]=lval[i]*a[l[i]]+f[l[i]][j];
    42             else f[i][j]=min(lval[i]*a[l[i]]+b[l[i]][deep[i]+1]+f[r[i]][j],rval[i]*a[r[i]]+b[r[i]][deep[i]+1]+f[l[i]][j]);
    43         }
    44     LL ans=f[1][0];
    45     for(int i=2;i<=n;i++)
    46     {
    47         int u=i,bro=u^1;
    48         LL tmp=f[u][deep[u]-1];
    49         while(u>1)
    50         {
    51             if(bro>n)tmp+=a[u>>2]*(dis[u>>1]-dis[u>>2]);
    52             else tmp+=a[bro]*(dis[bro]-dis[u>>1])+f[bro][deep[u>>1]-1];
    53             u>>=1,bro=u^1;
    54         }
    55         ans=min(ans,tmp);
    56     }
    57     printf("%lld
    ",ans);
    58 }
  • 相关阅读:
    一个JAVA题引发的思考
    eclipse好玩的插件集(一) CKEditor插件
    Log4J使用实例---日志进行邮件发送或是存入数据库
    log4j输出到数据库(输出自定义参数、分级保存)
    String和StringBuffer的一点研究
    String、StringBuffer、StringBuilder区分和性能比较
    最新eclipse安装SVN插件
    jsoup select 选择器
    网页导出excel文件
    Dom4j完整教程
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7348147.html
Copyright © 2011-2022 走看看