小凸玩密室题解

Posted ljk123-de-bo-ke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小凸玩密室题解相关的知识,希望对你有一定的参考价值。

小凸玩密室题解

恶心题啊~~
开始连题意都看不懂,
看了会题解的题意简化,结果理解错了题意,说多了都是泪啊~
首先说说题意吧:
点亮一盏灯后,只有点亮完子树内所有灯后才能点其他灯,而且点亮的灯要求要连通。
-->下一步一定点两个儿子之一,先点完这个儿子的子树再点另一个儿子。
然而,上一盏灯点什么十分不好求,贡献算不出(一个点子树内有多个层数相同的子孙)。
但是,我们可以通过上一盏灯算下一盏灯的贡献啊(每个点只有一个层数一定的父辈)。
所以点完这个点后,我们只有两种情况:
1.我们点完这个点后构成了一棵子树,我们下一步只可能点子树的根的父亲(点亮的灯要连通)。
2.点完了一棵子树加上其根的父亲,下一步一定点另一棵子树
并且,我们注意看题,发现:
什么?完全二叉树--->层数((log2 n))
于是我们设(dp)状态:
(f[i][j][0]:)先点完(i)的子树,然后先点(i)的第(j)个父亲的最小值;
(f[i][j][1]:)先点完(i)的子树,然后先点(i)的第(j)个父亲的另一个儿子的最小值。
并要预处理:(dis[i][j])表示第(i)个点到第(j)个祖先的边权值
怎么转移?
分三种情况讨论:
注:下面((i>>j))表示第(i)个点的第(j)个祖先的编号,((i>>(j-1))^1)表示第(i)个点第(j)个祖先的另一个儿子的编号。
1.一个儿子也没有:即(((i<<1)>n))
只可能从它结束到祖先。
(f[i][j][0]=dis[i][j]*num[i>>j],f[i][j][1]=(dis[i][j]+dis[(i>>(j-1))^1][1])*num[(i>>(j-1))^1])
2.如果只有一个儿子(左儿子):即((i<<1|1)>n)
必须从儿子继承。
(f[i][j][0]=dis[i<<1][1]*w[i<<1]+f[i<<1][j+1][0],f[i][j][1]=dis[i<<1][1]*w[i<<1]+f[i<<1][j+1][1])
2.如果有两个儿子:
那么可以先左后右,或者先右后左。
(f[i][j][0]=min(f[i][j][0],f[i<<1][1][1]+f[i<<1|1][j+1][0]+dis[i<<1][1]*w[i<<1]))
(f[i][j][0]=min(f[i][j][0],f[i<<1][j+1][0]+f[i<<1|1][1][1]+dis[i<<1|1][1]*w[i<<1|1]))
(f[i][j][1]=min(f[i][j][1],f[i<<1][1][1]+f[i<<1|1][j+1][1]+dis[i<<1][1]*w[i<<1]))
(f[i][j][1]=min(f[i][j][1],f[i<<1][j+1][1]+f[i<<1|1][1][1]+dis[i<<1|1][1]*w[i<<1|1]))
最后,我们只要确定了一个起始点,点亮顺序便确定了,按顺序点灯,答案取最小值即可。

#include<bits/stdc++.h>
#define ll long long
#define lc (i<<1)
#define rc (i<<1|1)
using namespace std;
const int N=2e5+7;
int n,lat;
ll tmp=0,ans=9e18+7,w[N],f[N][20][2],dis[N][20];
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;
}
int main(){
   n=read(),memset(f,0x3f,sizeof(f));
   for(int i=1;i<=n;++i) w[i]=read();
   for(int i=2;i<=n;++i){
       dis[i][1]=read();
       for(int j=2;(1<<(j-1))<=i;++j) dis[i][j]=dis[i>>1][j-1]+dis[i][1]; 
   }
   for(int i=n;i>=1;--i)
      for(int j=1;(1<<(j-1))<=i;++j){
          if(lc>n) f[i][j][0]=dis[i][j]*w[i>>j],f[i][j][1]=(dis[i][j]+dis[(i>>(j-1))^1][1])*w[(i>>(j-1))^1];
          else if(rc>n) f[i][j][0]=dis[lc][1]*w[lc]+f[lc][j+1][0],f[i][j][1]=dis[lc][1]*w[lc]+f[lc][j+1][1];
          else{
             f[i][j][0]=min(f[i][j][0],f[lc][1][1]+f[rc][j+1][0]+dis[lc][1]*w[lc]),f[i][j][0]=min(f[i][j][0],f[lc][j+1][0]+f[rc][1][1]+dis[rc][1]*w[rc]);
             f[i][j][1]=min(f[i][j][1],f[lc][1][1]+f[rc][j+1][1]+dis[lc][1]*w[lc]),f[i][j][1]=min(f[i][j][1],f[lc][j+1][1]+f[rc][1][1]+dis[rc][1]*w[rc]);
          }
      }
   for(int i=1;i<=n;++i){
       tmp=f[i][1][0],lat=i;
       for(int j=(i>>1);j;j>>=1){
           if((lat^1)<=n) tmp+=dis[lat^1][1]*w[lat^1]+f[lat^1][2][0];
           else tmp+=dis[j][1]*w[j>>1];
           lat=j;
       }
       ans=min(ans,tmp);
   }
   printf("%lld
",ans);
   return 0;
}

以上是关于小凸玩密室题解的主要内容,如果未能解决你的问题,请参考以下文章

—Libre#2009. 「SCOI2015」小凸玩密室

LibreOJ #2009. 「SCOI2015」小凸玩密室

「SCOI2015」小凸玩密室

[BZOJ4446]SCoi2015 小凸玩密室 树形DP(烧脑高能预警)

bzoj4446 [Scoi2015]小凸玩密室

[bzoj4446] [loj#2009] [Scoi2015] 小凸玩密室