大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。江山如画,一时多少豪杰。遥想公瑾当年,小乔初嫁了,雄姿英发。羽扇纶巾,谈笑间,樯橹灰飞烟灭。故国神游,多情应笑我,早生华发。人生如梦,一尊还酹江月。

树形DP中的实用卡空间技巧——洛谷P4438 [HNOI/AHOI2018]道路

若DP统计答案时是自下而上的,则两个子节点在这个节点统计完答案后就用不上了,所以我们可以设x的dfs序为dfn,左儿子的为dfn+1,右儿子dfn+2。这样计算出的结果除了根节点和少数节点外,其他节点的答案都是错的

适用于只用知道根节点的DP值且从下至上更新答案的情况

具体代码如下

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define fo(i,a) for(int i=0;i<a;++i)

const int inf=0x3f3f3f3f,N=4e4+10;
typedef long long ll;

int n,ls[N],rs[N],a[N],b[N],c[N];
ll dp[100][50][50];

inline void read(int &x){
	x=0;char c=getchar(),f=1;
	while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
	x*=f;
}

void dfs(int u,int x,int y,int id){
	if(u>=n){
		go(i,0,x)
			go(j,0,y)
				dp[id][i][j]=1ll*c[u]*(a[u]+i)*(b[u]+j);
		return;
	}
	dfs(ls[u],x+1,y,id+1),dfs(rs[u],x,y+1,id+2);
	go(i,0,x)
		go(j,0,y)
			dp[id][i][j]=min(dp[id+1][i+1][j]+dp[id+2][i][j],dp[id+1][i][j]+dp[id+2][i][j+1]);
}

int main(){
	read(n);
	int x,y;
	go(i,1,n-1){
		read(x),read(y),x=(x<0)?(-x+n-1):x,y=(y<0)?(-y+n-1):y;
		ls[i]=x,rs[i]=y;
	}
	go(i,n,2*n-1) read(a[i]),read(b[i]),read(c[i]);
	dfs(1,0,0,1);
	printf("%lld",dp[1][0][0]);
	return 0;
}
posted @ 2019-10-22 22:07  White_star  阅读(156)  评论(0编辑  收藏  举报
}