[SCOI2015]小凸玩密室

Posted by Dispwnl on April 1, 2019

题目

题目描述

小凸和小方相约玩密室逃脱,这个密室是一棵有 nn 个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯泡即可逃出密室。每个灯泡有个权值 aia_i,每条边也有个权值 bib_i。点亮第一个灯泡不需要花费,之后每点亮一个新的灯泡 vv 的花费,等于上一个被点亮的灯泡 uu 到这个点 vv 的距离 Du,vD_{u,v},乘以这个点的权值 ava_v。在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。

输入输出格式

输入格式:

11 行包含 11 个数 nn,代表节点的个数

22 行包含 nn 个数,代表每个节点的权值 aia_i(i=1,2,,n)(i = 1, 2,……, n)

33 行包含 n1n - 1 个数,代表每条边的权值 bib_i,第 ii 号边是由第 (i+1)/2(i+1)/2 号点连向第 i+1i + 1 号点的边。(i=1,2,,n1)(i = 1, 2,……, n - 1)

输出格式:

输出包含 11 个数,代表最少的花费。

输入输出样例

输入样例#1:

3
5 1 2
2 1

输出样例#1:

5

说明

对于 10%10\% 的数据, 1n101 \leq n \leq 10

对于 20%20\% 的数据, 1n201 \leq n \leq 20

对于 30%30\% 的数据, 1n20001 \leq n \leq 2000

对于 100%100\% 的数据, 1n2105,1ai,bi1051 \leq n \leq 2 * 10^5, 1 \leq a_i, b_i \leq 10^5

题解

因为是完全二叉树,树的高度为logn\log n

fi,jf_{i,j}表示搞了点ii最后到ii的深度为jj的祖先需要的最少花费,gi,jg_{i,j}表示搞了点ii最后到ii的深度为jj的祖先的兄弟需要的最小花费

转移就从叶子一路转移上去即可

最后枚举起始点,统计一路跳到根的花费

因为这个题的树的构造很有特点,所以可以不用建树,用位运算搞搞各个点的关系就出来了

代码

# 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;
}