bzoj4446:[Scoi2015]小凸玩密室
传送门
神仙题吧,很有东西
看了题解才会做的
先提取有用信息:
1、这个密室是一棵有n个节点的完全二叉树
2、在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通
3、在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡
所以get到信息
1、树高严格\(logn\)
2、点亮的灯泡组成一个联通块
3、点亮一个灯泡后一定是往儿子走
然而我依然不会,借助题解才设出了状态
\(f[i][j][0]\)表示第一次点第\(i\)个节点,点亮完\(i\)的子树后点亮\(i\)的第\(j\)个祖先的最小花费
\(f[i][j][1]\)表示第一次点第\(i\)个节点,点亮完\(i\)的子树后点亮\(i\)的第\(j\)个祖先的另一个儿子的最小花费(就是\(i\)节点所在子树的兄弟节点)
预处理出每个节点的所有祖先,
然后考虑怎么转移,分三种情况
1、没有儿子的,算出它到它的所有祖先的花费
2、只有左儿子或只有右儿子的,只能往左或者右走
3、有两个儿子的,分情况讨论一下
由于第一次点的点不确定,所以要枚举一下,然后一直往上走就行了,碰到有2个儿子的判断一下,就行了
方程我就不写了,这位大佬方程写的很详细传送门
时间复杂度\(O(nlogn)\)
千万别看我代码,去看那些非递归转移的吧,我的代码已经魔改的看不得了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=2e5+10;
int n,a[maxn],b[maxn],l[maxn],r[maxn],g[maxn][20],f[maxn][20][2],dep[maxn],ans;
void prepare(int x)
{
int now=x,tot=1;
while(now)now=g[now][1],tot++,g[x][tot]=now;
if(l[x])g[l[x]][1]=x,dep[l[x]]=dep[x]+b[l[x]-1],prepare(l[x]);
if(r[x])g[r[x]][1]=x,dep[r[x]]=dep[x]+b[r[x]-1],prepare(r[x]);
}
void dfs(int x)
{
if(!l[x]&&!r[x])
{
int now=x,las;
for(rg int i=1;i<20;i++)
{
las=now,now>>=1;
if(!now)break;
f[x][i][0]=(dep[x]-dep[now])*a[now];
if(l[now]&&r[now])
{
if(l[now]!=las)f[x][i][1]=(dep[x]+dep[l[now]]-2*dep[now])*a[l[now]];
else f[x][i][1]=(dep[x]+dep[r[now]]-2*dep[now])*a[r[now]];
}
else f[x][i][1]=1e18;
}
}
else
{
if(l[x]&&!r[x])
{
dfs(l[x]);
f[x][0][0]=1e18;
f[x][0][0]=min(f[x][0][0],f[l[x]][0][0]+(dep[l[x]]-dep[x])*a[l[x]]);
for(rg int i=1;i<20;i++)
{
if(!g[x][i])break;
f[x][i][0]=f[x][i][1]=1e18;
f[x][i][0]=min(f[x][i][0],f[l[x]][i+1][0]+(dep[l[x]]-dep[x])*a[l[x]]);
f[x][i][1]=min(f[x][i][1],f[l[x]][i+1][1]+(dep[l[x]]-dep[x])*a[l[x]]);
}
}
else if(r[x]&&!l[x])
{
dfs(r[x]);
f[x][0][0]=1e18;
f[x][0][0]=min(f[x][0][0],f[r[x]][0][0]+(dep[r[x]]-dep[x])*a[r[x]]);
for(rg int i=1;i<20;i++)
{
if(!g[x][i])break;
f[x][i][0]=f[x][i][1]=1e18;
f[x][i][0]=min(f[x][i][0],f[r[x]][i+1][0]+(dep[r[x]]-dep[x])*a[r[x]]);
f[x][i][1]=min(f[x][i][1],f[r[x]][i+1][1]+(dep[r[x]]-dep[x])*a[r[x]]);
}
}
else
{
dfs(l[x]),dfs(r[x]);
f[x][0][0]=1e18;
f[x][0][0]=min(f[x][0][0],f[l[x]][1][1]+(dep[l[x]]-dep[x])*a[l[x]]+f[r[x]][0][0]);
f[x][0][0]=min(f[x][0][0],f[r[x]][1][1]+(dep[r[x]]-dep[x])*a[r[x]]+f[l[x]][0][0]);
for(rg int i=1;i<20;i++)
{
if(!g[x][i])break;
f[x][i][0]=f[x][i][1]=1e18;
f[x][i][0]=min(f[x][i][0],f[l[x]][1][1]+(dep[l[x]]-dep[x])*a[l[x]]+f[r[x]][i+1][0]);
f[x][i][0]=min(f[x][i][0],f[r[x]][1][1]+(dep[r[x]]-dep[x])*a[r[x]]+f[l[x]][i+1][0]);
f[x][i][1]=min(f[x][i][1],f[l[x]][1][1]+(dep[l[x]]-dep[x])*a[l[x]]+f[r[x]][i+1][1]);
f[x][i][1]=min(f[x][i][1],f[r[x]][1][1]+(dep[r[x]]-dep[x])*a[r[x]]+f[l[x]][i+1][1]);
}
}
}
}
signed main()
{
read(n);
for(rg int i=1;i<=n;i++)read(a[i]);
for(rg int i=1;i<n;i++)
{
read(b[i]);
if(!l[(i+1)/2])l[(i+1)/2]=i+1;
else r[(i+1)/2]=i+1;
}
prepare(1),dfs(1);ans=f[1][0][0];
for(rg int i=2;i<=n;i++)
{
int las,now=i,val=f[i][1][0];
while(g[now][1]!=1)
{
las=now,now=g[now][1];
if(l[now]&&r[now])
{
if(las!=l[now])val+=(dep[l[now]]-dep[now])*a[l[now]]+f[l[now]][2][0];
else val+=(dep[r[now]]-dep[now])*a[r[now]]+f[r[now]][2][0];
}
else
{
if(las!=i)val+=(dep[las]-dep[now])*a[now];
val+=(dep[now]-dep[g[now][1]])*a[g[now][1]];
}
}
las=now,now=g[now][1];
if(l[now]&&r[now])
{
if(las!=l[now])val+=(dep[l[now]]-dep[now])*a[l[now]]+f[l[now]][0][0];
else val+=(dep[r[now]]-dep[now])*a[r[now]]+f[r[now]][0][0];
}
else if(las!=i)val+=(dep[las]-dep[now])*a[now];
ans=min(ans,val);
}
printf("%lld\n",ans);
}