SCOI2015 小凸玩密室

小凸玩密室

小凸和小方相约玩密室逃脱,这个密室是一棵有\(n\)个节点的完全二叉树,每个节点有一个灯泡。点亮所有灯泡即可逃出密室。每个灯泡有个权值\(a_i\),每条边也有个权值\(b_i\)

点亮第\(1\)个灯泡不需要花费,之后每点亮一个新的灯泡\(v\)的花费,等于上一个被点亮的灯泡\(u\)到这个点\(v\)的距离\(\operatorname{dist}(u, v)\),乘以这个点的权值\(a_v\)

在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。请告诉他们,逃出密室的最少花费是多少。

\(1 \leq n \leq 2 \times 10 ^ 5, 1 < a_i, b_i \leq 10 ^ 5\)

题解

https://www.cnblogs.com/CXCXCXC/p/5312237.html

\(f(x,y)\)表示走完\(x\)及其子树再走到\(y\)的最小代价。考虑转移方式,

  • 如果\(x\)是叶子节点,那么答案就是\(a_y\operatorname{dist}(x,y)\)

  • 如果\(x\)只有左孩子,就先遍历左子树,状态转移是\(a_{\text{lc}}b_{\text{lc}}+f(\text{lc},y)\)

  • 如果两个孩子都有,就考虑是先转移到左孩子还是右孩子,\(f(x,y)=\min(a_{\text{lc}}b_{\text{lc}}+f(\text{lc},\text{rc})+f(\text{rc},y),a_{\text{rc}}b_{\text{rc}}+f(\text{rc},\text{lc})+f(\text{lc},y))\)

题目数据范围很大,这样的转移肯定在时间和空间上都不行。

但是发现很多\(x,y\)是不合法的,比如\(y\)肯定不能是\(x\)的子节点。由此考虑有意义的\(x,y\),可以发现,\(y\)一定是某个\(x\)的祖先的另一个儿子,

所以重新定义\(f(x,i)\)为走完\(x\)及其子树到深度为\(i\)的节点的子节点的最小代价。

\(g(x,i)\)表示走完\(x\)的子树再走到\(x\)的深度为\(i\)的祖先的最小代价。转移:

  • 叶子:\(g(x,i)=a_y\operatorname{dist}(x,y)\)

  • 只有左孩子:\(g(x,i)=a_{\text{lc}}b_{\text{lc}}+g(\text{lc},i)\)

  • 左右孩子都有:\(g(x,i)=\min(a_{\text{lc}}b_{\text{lc}}+f(\text{lc},\text{dep}_x)+g(\text{rc},i),a_{\text{rc}}b_{\text{rc}}+f(\text{rc},\text{dep}_x)+g(\text{lc},i))\)

最后就是统计答案了,我们选定一个节点\(x\)作为第一个点亮的灯,然后统计其子树的答案,再走到\(x\)的父节点,再走\(x\)的兄弟节点……以此类推。

由于\(f\)\(g\)的第一维都是\(n\),第二维都是\(\log n\),所以总的时间复杂度是\(O(n\log n)\)

CO int N=2e5+10;
int a[N],b[N],dep[N],dis[N];
int f[N][19],g[N][19];

signed main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=2;i<=n;++i) read(b[i]);
	dep[1]=1;
	for(int i=2;i<=n;++i) dep[i]=dep[i>>1]+1,dis[i]=dis[i>>1]+b[i];
	for(int x=n;x>=1;--x)for(int i=0;i<dep[x];++i){
		int lc=x<<1,rc=x<<1|1;
		int y=x>>(dep[x]-i-1)^1;
		if(lc>n) f[x][i]=a[y]*(dis[x]+dis[y]-2*dis[y>>1]);
		else if(rc>n) f[x][i]=a[lc]*b[lc]+f[lc][i];
		else f[x][i]=min(a[lc]*b[lc]+f[lc][dep[x]]+f[rc][i],a[rc]*b[rc]+f[rc][dep[x]]+f[lc][i]);
	}
	for(int x=n;x>=1;--x)for(int i=0;i<=dep[x];++i){
		int lc=x<<1,rc=x<<1|1;
		int y=x>>(dep[x]-i);
		if(lc>n) g[x][i]=a[y]*(dis[x]-dis[y]);
		else if(rc>n) g[x][i]=a[lc]*b[lc]+g[lc][i];
		else g[x][i]=min(a[lc]*b[lc]+f[lc][dep[x]]+g[rc][i],a[rc]*b[rc]+f[rc][dep[x]]+g[lc][i]);
	}
	int ans=g[1][0];
	for(int i=2;i<=n;++i){
		int sum=g[i][dep[i]-1];
		for(int x=i;x>1;x>>=1){
			int y=x^1,z=x>>1;
			if(y>n) sum+=a[z>>1]*b[z];
			else sum+=a[y]*b[y]+g[y][dep[z]-1];
		}
		ans=min(ans,sum);
	}
	printf("%lld\n",ans);
	return 0;
}

posted on 2020-06-08 10:51  autoint  阅读(155)  评论(0编辑  收藏  举报

导航