题目分析
现在我们用玩密室逃脱的思路来解决这题吧QWQ
1.寻找信息
1.密室
2.完全二叉树
3.在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通
4.在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡
5.点一个节点的费用的计算方式和上一个点亮的节点有关
2.拟合信息
假设我们现在点完了以x为根的子树,由信息3,我们必须点亮x的父亲节点o。由信息4和2,我们下一步就必须点亮o的另一个儿子y,点完了y子树后,就要点亮o的父亲……
由信息5,我们dp的时候所需的状态一定要包括上一个节点是谁。
现在我们看我们要如何计算答案。可以想到先枚举最开始点亮的节点,然后模拟点灯过程。这时候我们需要一个这样的状态:g(x,i)先点以x为根的子树,再点x的位于第i层的祖先所花的费用。
于是计算答案是这样的:
ans=g[1][0];
for(i=2;i<=n;++i) {
LL s=g[i][dep[i]-1];
for(x=i;x>1;x>>=1) {
y=x^1,o=x>>1;
if(y>n) s+=b[o]*a[o>>1];//没有右儿子
else s+=a[y]*b[y]+g[y][dep[o]-1];
}
if(s<ans) ans=s;
}
3.分析所需信息
于是我们的目标是算出g(x,i)。
首先,x的位于第i层的祖先我们给它取名叫y。并用dis[x]表示从根节点到x的距离。
如果x是叶子节点,非常简单:g(x,i)=(dis[x]-dis[y])*a[y](只用计算点y的费用,点x的费用是在别的状态里计算的)。如果i=0则g(x,i)=0(第一次点灯)
可是如果x不是叶子节点呢?我们就需要先点完x,再去点x的兄弟,再点祖先啊!这样状态不好转移了。
于是想到我们还需要一个状态f(x,i):以x为根的子树刚刚点完后,去点第i层的兄弟的花费。(如图所示,要点的那个节点依然叫y)
好,我们先不管f是怎么求的,先继续分析g。
有了f的帮助,我们就可以轻松写出求g的方程了!
如果x只有左儿子:
g[x][i]=a[lson[x]]∗b[lson[x]]+g[lson[x]][i];
如果x有左右儿子:
g[x][i]=min(a[lson[x]]∗b[lson[x]]+f[lson[x]][dep[x]+1]+g[rson[x]][i],a[rson[x]]∗b[rson[x]]+f[rson[x]][dep[x]+1]+g[lson[x]][i]);
好长的状态转移方程QAQ…
for(x=n;x>=1;--x)
for(i=0;i<=dep[x];++i) {
if((x<<1)>n) {
y=x>>(dep[x]-i);
if(i) g[x][i]=(dis[x]-dis[y])*a[y];
else g[x][i]=0;
}
else if((x<<1)==n) g[x][i]=a[n]*b[n]+g[n][i];
else {
int l=(x<<1),r=(x<<1)|1,d=dep[x]+1;
g[x][i]=min(a[l]*b[l]+f[l][d]+g[r][i],a[r]*b[r]+f[r][d]+g[l][i]);
}
}
4.搜寻所需信息
现在只差f没有求出来啦!怎么求QAQ
仔细想想就会发现,和求g的转移方程不是差不多吗?
至此,我们已经完美地从密室出逃解决此题。
for(x=n;x>1;--x)
for(i=2;i<=dep[x];++i) {
if((x<<1)>n){
o=x>>(dep[x]-i+1),y=(x>>(dep[x]-i))^1;
f[x][i]=(dis[x]+dis[y]-dis[o]-dis[o])*a[y];
}
else if((x<<1)==n) f[x][i]=a[n]*b[n]+f[n][i];
else {
int l=(x<<1),r=(x<<1)|1,d=dep[x]+1;
f[x][i]=min(a[l]*b[l]+f[l][d]+f[r][i],a[r]*b[r]+f[r][d]+f[l][i]);
}
}
代码
虽然写的长了一点,但是每一行都没有超过80列被怼线哦耶!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
LL read() {
LL q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
return q;
}
const int N=200005;
int n;LL ans;
LL a[N],b[N],f[N][19],g[N][19],dis[N],dep[N];
int main()
{
int i,x,o,y;
n=read();
for(i=1;i<=n;++i) a[i]=read();dep[1]=1;
for(i=2;i<=n;++i){
b[i]=read();
dis[i]=dis[i>>1]+b[i],dep[i]=dep[i>>1]+1;
}
for(x=n;x>1;--x)
for(i=2;i<=dep[x];++i) {
if((x<<1)>n){
o=x>>(dep[x]-i+1),y=(x>>(dep[x]-i))^1;
f[x][i]=(dis[x]+dis[y]-dis[o]-dis[o])*a[y];
}
else if((x<<1)==n) f[x][i]=a[n]*b[n]+f[n][i];
else {
int l=(x<<1),r=(x<<1)|1,d=dep[x]+1;
f[x][i]=min(a[l]*b[l]+f[l][d]+f[r][i],a[r]*b[r]+f[r][d]+f[l][i]);
}
}
for(x=n;x>=1;--x)
for(i=0;i<=dep[x];++i) {
if((x<<1)>n) {
y=x>>(dep[x]-i);
if(i) g[x][i]=(dis[x]-dis[y])*a[y];
else g[x][i]=0;
}
else if((x<<1)==n) g[x][i]=a[n]*b[n]+g[n][i];
else {
int l=(x<<1),r=(x<<1)|1,d=dep[x]+1;
g[x][i]=min(a[l]*b[l]+f[l][d]+g[r][i],a[r]*b[r]+f[r][d]+g[l][i]);
}
}
ans=g[1][0];
for(i=2;i<=n;++i) {
LL s=g[i][dep[i]-1];
for(x=i;x>1;x>>=1) {
y=x^1,o=x>>1;
if(y>n) s+=b[o]*a[o>>1];
else s+=a[y]*b[y]+g[y][dep[o]-1];
}
if(s<ans) ans=s;
}
printf("%lld",ans);
return 0;
}