题目描述
小凸和小方相约玩密室逃脱,这个密室是一棵有 n n n 个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯泡即可逃出密室。每个灯泡有个权值 a i a_i ai,每条边也有个权值 b i b_i bi。点亮第一个灯泡不需要花费,之后每点亮一个新的灯泡 v v v 的花费,等于上一个被点亮的灯泡 u u u 到这个点 v v v 的距离 D u , v D_{u,v} Du,v,乘以这个点的权值 a v a_v av。在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。
输入输出格式
输入格式:
第
1
1
1 行包含
1
1
1 个数
n
n
n,代表节点的个数
第
2
2
2 行包含
n
n
n 个数,代表每个节点的权值
a
i
a_i
ai。
(
i
=
1
,
2
,
…
…
,
n
)
(i=1,2,……, n)
(i=1,2,……,n)
第 33 行包含
n
−
1
n−1
n−1 个数,代表每条边的权值
b
i
b_i
bi,第
i
i
i 号边是由第
(
i
+
1
)
/
2
(i+1)/2
(i+1)/2 号点连向第
i
+
1
i+1
i+1 号点的边。
(
i
=
1
,
2
,
…
…
,
n
−
1
)
(i=1,2,……, n - 1)
(i=1,2,……,n−1)
输出格式:
输出包含
1
1
1 个数,代表最少的花费。
输入输出样例
输入样例#1:
3
5 1 2
2 1
输出样例#1:
5
说明
对于
10
%
10\%
10% 的数据,
1
≤
n
≤
10
1≤n≤10
1≤n≤10
对于
20
%
20\%
20% 的数据,
1
≤
n
≤
20
1≤n≤20
1≤n≤20
对于
30
%
30\%
30% 的数据,
1
≤
n
≤
2000
1≤n≤2000
1≤n≤2000
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
2
∗
1
0
5
1≤n≤2∗10^5
1≤n≤2∗105,
1
≤
a
i
,
b
i
≤
1
0
5
1≤a_i,b_i≤10^5
1≤ai,bi≤105
分析:
因为点亮的灯必须是一个连通块,而且子树内也必须全部点亮才能点别的,所以点亮顺序一定是先从某个点
x
x
x
开始,点完他的子树,然后再点亮他的父亲,然后点他的兄弟(没有跳过),然后再点父亲的父亲……
我们设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 为从点
i
i
i走到他的
j
j
j级祖先的另一个儿子的代价。
g
[
i
]
[
j
]
g[i][j]
g[i][j] 为从点
i
i
i走到他的
j
j
j级祖先的代价。
对于一个叶子节点,无任何代价就可以乱走,代价就直接是距离
∗
*
∗终点的权值。
对于一个只有左儿子的节点,那么这个节点子树内一定只有两个点。那么先要走到这个左儿子,因为左儿子是也只节点,直接加上左儿子对应转移值。
对于其他节点,先考虑
f
f
f 值的转移。假设当前点为
x
x
x,我们先往左走,花费代价就是这两点距离
∗
*
∗终点权值,然后再从左儿子走到右儿子,也就是
x
.
l
s
o
n
x.lson
x.lson的
1
1
1级祖先的另一个儿子,即
f
[
x
.
l
s
o
n
]
[
1
]
f[x.lson][1]
f[x.lson][1],然后再从右儿子走到目标点的另一个儿子得到
f
[
x
]
[
j
]
f[x][j]
f[x][j],因为
x
x
x要走
j
j
j层,那么儿子就要走
j
+
1
j+1
j+1层。
转移为
f
[
i
]
[
j
]
=
d
i
s
(
i
,
i
.
l
s
o
n
)
∗
w
[
i
.
l
s
o
n
]
+
f
[
i
.
l
s
o
n
]
[
1
]
+
f
[
i
.
r
s
o
n
]
[
j
+
1
]
f[i][j]=dis(i,i.lson)*w[i.lson]+f[i.lson][1]+f[i.rson][j+1]
f[i][j]=dis(i,i.lson)∗w[i.lson]+f[i.lson][1]+f[i.rson][j+1]
如果走右儿子同理,两者取
m
i
n
min
min。
g
g
g 值也一样处理,不过这个要用到
f
f
f 值。
那么从
x
x
x点开始的答案就是走到
0
0
0 号点(假设是
1
1
1 的父亲)的代价,因为最后一步是从任意一定一点(叶子节点)结束,不好统计,又因为
0
0
0 号点的权值为
0
0
0,所以走到
0
0
0号点没有费用,所以直接可以看做答案。
复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e5+7;
using namespace std;
LL a[maxn],b[maxn],lc[maxn],rc[maxn];
LL n;
LL g[maxn][20],f[maxn][20],dis[maxn][20];
LL ans,sum;
LL brother(LL k,LL x)
{
return (k>>(x-1))^1;
}
int main()
{
scanf("%lld",&n);
for (LL i=1;i<=n;i++) scanf("%lld",&a[i]);
for (LL i=2;i<=n;i++) scanf("%lld",&dis[i][1]);
for (LL i=1;i<=n;i++)
{
if (i*2<=n) lc[i]=i*2;
if (i*2+1<=n) rc[i]=i*2+1;
}
for (LL j=2;j<20;j++)
{
for (LL i=1;i<=n;i++)
{
dis[i][j]=dis[i][j-1]+dis[i>>(j-1)][1];
}
}
for (LL i=n;i>0;i--)
{
if (!lc[i])
{
for (LL j=1;i>>(j-1);j++)
{
f[i][j]=(dis[i][j]+dis[brother(i,j)][1])*a[brother(i,j)];
}
}
else if (!rc[i])
{
for (LL j=1;i>>(j-1);j++)
f[i][j]=dis[lc[i]][1]*a[lc[i]]+f[lc[i]][j+1];
}
else
{
for (LL j=1;i>>(j-1);j++)
f[i][j]=min(dis[lc[i]][1]*a[lc[i]]+f[lc[i]][1]+f[rc[i]][j+1],dis[rc[i]][1]*a[rc[i]]+f[rc[i]][1]+f[lc[i]][j+1]);
}
}
for (LL i=n;i>0;i--)
{
if (!lc[i])
{
for (LL j=1;i>>(j-1);j++)
g[i][j]=dis[i][j]*a[i>>j];
}
else if (!rc[i])
{
for (LL j=1;i>>(j-1);j++)
g[i][j]=g[lc[i]][j+1]+dis[lc[i]][1]*a[lc[i]];
}
else
{
for (LL j=1;i>>(j-1);j++)
g[i][j]=min(dis[lc[i]][1]*a[lc[i]]+f[lc[i]][1]+g[rc[i]][j+1],dis[rc[i]][1]*a[rc[i]]+f[rc[i]][1]+g[lc[i]][j+1]);
}
}
ans=1e18;
for (LL i=1;i<=n;i++)
{
sum=g[i][1];
for (LL x=i>>1,j=1;x>0;x>>=1,j++)
{
LL fa=x>>1;
if (brother(i,j)>n) sum+=dis[x][1]*a[fa];
else sum+=dis[brother(i,j)][1]*a[brother(i,j)]+g[brother(i,j)][2];
}
ans=min(ans,sum);
}
printf("%lld",ans);
}