小凸玩密室题解
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;
}
以上是关于小凸玩密室题解的主要内容,如果未能解决你的问题,请参考以下文章
LibreOJ #2009. 「SCOI2015」小凸玩密室