Description
小凸和小方相约玩密室逃脱,这个密室是一棵有n个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯
泡即可逃出密室。每个灯泡有个权值Ai,每条边也有个权值bi。点亮第1个灯泡不需要花费,之后每点亮4
个新的灯泡V的花费,等于上一个被点亮的灯泡U到这个点V的距离Du,v,乘以这个点的权值Av。在点灯
的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。
Input
第1行包含1个数n,代表节点的个数
第2行包含n个数,代表每个节点的权值ai。(i=l,2,…,n)
第3行包含n-l个数,代表每条边的权值bi,第i号边是由第(i+1)/2号点连向第i+l号点的边。
(i=l,2…N-1)
Output
输出包含1个数,代表最少的花费。
Sample Input
3
5 1 2
2 1
Sample Output
5
HINT
对于100%的数据,1≤N≤2×105,1
分析
代码
#include <bits/stdc++.h>
const int N = 200005;
const int M = 18;
typedef long long ll;
int n,l[N];
ll a[N],b[N];
ll d[N];
ll f[N][M],g[N][M];
void dp()
{
for (int x = n; x; x--)
{
for (int i = l[x] - 1; i >= 0; i--)
{
int lc = x << 1, rc = lc + 1, target = x >> (l[x] - i - 1) ^ 1;
if (lc > n)
{
f[x][i] = a[target] * (d[target] + d[x] - (d[target >> 1] << 1));
}
else
if (rc > n)
{
f[x][i] = f[lc][i] + a[lc] * b[lc];
}
else
{
f[x][i] = std::min(
f[lc][l[x]] + a[lc] * b[lc] + f[rc][i],
f[rc][l[x]] + a[rc] * b[rc] + f[lc][i]);
}
}
}
for (int x = n; x; x--)
{
for (int i = l[x]; i >= 0; i--)
{
int lc = x << 1, rc = lc + 1, target = x >> (l[x] - i);
if (lc > n)
{
g[x][i] = a[target] * (d[x] - d[target]);
}
else
if (rc > n)
{
g[x][i] = g[lc][i] + a[lc] * b[lc];
}
else
{
g[x][i] = std::min(
a[lc] * b[lc] + f[lc][l[x]] + g[rc][i],
a[rc] * b[rc] + f[rc][l[x]] + g[lc][i]);
}
}
}
}
ll cal(int x)
{
ll res = g[x][l[x] - 1];
for (; x != 1; x >>= 1)
{
int brother = x ^ 1, father = x >> 1;
if (brother > n)
{
res += a[father >> 1] * b[father];
}
else
{
res += a[brother] * b[brother] + g[brother][l[father] - 1];
}
}
return res;
}
ll solve()
{
ll ans = g[1][0];
for (int i = 2; i <= n; i++)
{
ans = std::min(ans, cal(i));
}
return ans;
}
int main()
{
scanf("%d",&n);
for (int i = 1; i <= n; i++)
{
scanf("%lld",&a[i]);
}
for (int i = 2; i <= n; i++)
{
scanf("%lld",&b[i]);
}
l[1] = 1, d[1] = 0;
for (int i = 2; i <= n; i++)
{
l[i] = l[i >> 1] + 1;
d[i] = d[i >> 1] + b[i];
}
dp();
printf("%lld\n",solve());
}