码迷,mamicode.com
首页 > 其他好文 > 详细

小凸玩密室题解

时间:2019-11-11 09:23:17      阅读:96      评论:0      收藏:0      [点我收藏+]

标签:怎么   最小   else   efi   状态   lse   通过   eof   char   

小凸玩密室题解

恶心题啊~~
开始连题意都看不懂,
看了会题解的题意简化,结果理解错了题意,说多了都是泪啊~
首先说说题意吧:
点亮一盏灯后,只有点亮完子树内所有灯后才能点其他灯,而且点亮的灯要求要连通。
-->下一步一定点两个儿子之一,先点完这个儿子的子树再点另一个儿子。
然而,上一盏灯点什么十分不好求,贡献算不出(一个点子树内有多个层数相同的子孙)。
但是,我们可以通过上一盏灯算下一盏灯的贡献啊(每个点只有一个层数一定的父辈)。
所以点完这个点后,我们只有两种情况:
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\n",ans);
   return 0;
}

小凸玩密室题解

标签:怎么   最小   else   efi   状态   lse   通过   eof   char   

原文地址:https://www.cnblogs.com/ljk123-de-bo-ke/p/11832248.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!