题目
题目描述
小凸和小方相约玩密室逃脱,这个密室是一棵有 个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯泡即可逃出密室。每个灯泡有个权值 ,每条边也有个权值 。点亮第一个灯泡不需要花费,之后每点亮一个新的灯泡 的花费,等于上一个被点亮的灯泡 到这个点 的距离 ,乘以这个点的权值 。在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。
输入输出格式
输入格式:
第 行包含 个数 ,代表节点的个数
第 行包含 个数,代表每个节点的权值 。
第 行包含 个数,代表每条边的权值 ,第 号边是由第 号点连向第 号点的边。
输出格式:
输出包含 个数,代表最少的花费。
输入输出样例
输入样例#1:
3
5 1 2
2 1
输出样例#1:
5
说明
对于 的数据,
对于 的数据,
对于 的数据,
对于 的数据,
题解
因为是完全二叉树,树的高度为
设表示搞了点最后到的深度为的祖先需要的最少花费,表示搞了点最后到的深度为的祖先的兄弟需要的最小花费
转移就从叶子一路转移上去即可
最后枚举起始点,统计一路跳到根的花费
因为这个题的树的构造很有特点,所以可以不用建树,用位运算搞搞各个点的关系就出来了
代码
# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define tl (i<<1)
# define tr (i<<1|1)
# define F (i>>d[i]-j)
# define LL long long
using namespace std;
const int MAX=2e5+5;
int n;
LL ans=1e18,Ans;
int d[MAX],a[MAX],b[MAX],D[MAX];
LL f[20][MAX],g[20][MAX];
int read()
{
int x(0);
char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
return x;
}
void GET_DP()
{
for(int i=n;i>=1;--i)
for(int j=0;j<19;++j)
if(tl>n) f[j][i]=1ll*(D[i]-D[F])*a[F],g[j][i]=1ll*(D[i]-D[F]+b[F]+b[F^1])*a[F^1];
else if(tl==n) f[j][i]=1ll*a[tl]*b[tl]+f[j][tl],g[j][i]=1ll*a[tl]*b[tl]+g[j][tl];
else f[j][i]=min(1ll*a[tl]*b[tl]+g[d[tl]][tl]+f[j][tr],1ll*a[tr]*b[tr]+g[d[tr]][tr]+f[j][tl]),g[j][i]=min(1ll*a[tl]*b[tl]+g[d[tl]][tl]+g[j][tr],1ll*a[tr]*b[tr]+g[d[tr]][tr]+g[j][tl]);
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=2;i<=n;++i)
b[i]=read();
for(int i=1;i<=n;++i)
d[i]=d[i>>1]+1,D[i]=D[i>>1]+b[i];
GET_DP();
for(int x=1;x<=n;++x)
{
Ans=f[d[x]-1][x];
for(int i=x;i>1;i>>=1)
if((i^1)>n) Ans+=1ll*b[i>>1]*a[i>>2];
else Ans+=d[i^1]<2?0:f[d[i^1]-2][i^1]+1ll*b[i^1]*a[i^1];
ans=min(ans,Ans);
}
return printf("%lld",ans),0;
}