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);
}
posted @ 2019-03-09 16:32  蒟蒻--lichenxi  阅读(167)  评论(0编辑  收藏  举报