[HAOI2010]软件安装 [Tarjan + 树形DP]

传送门

我们先Tarjan 缩点 , 然后树形背包

我们令f[u][i] 表示点u及儿子选i个的最大价值

我们发现如果要加上儿子的贡献 , 当前点u必须选

f[u][i]=max(f[son][j]+f[u][i-j]) _{(w[u]<=i<=m)}_{(0<=j<=i-w[u])}

因为u必须选 , 所以j只能枚举到i-w[u]

DP方程固然重要 , 范围也很重要

化学方程式固然重要 , 反应条件 , 气体符号 , 沉淀符号也很重要

#include<bits/stdc++.h>
#define N 105
#define M 505
using namespace std;
int w[N],v[N],ww[N],vv[N],n,m;
int du[N],f[N][M],rt,siz[N];
vector<int> V[N];
int first[N],next[N*2],to[N*2],tot;
int dfn[N],low[N],sta[N];
int top,insta[N],id[N],sign,cnt;
void add(int x,int y){
	next[++tot]=first[x],first[x]=tot,to[tot]=y;
}
void dfs(int u){
	low[u] = dfn[u] = ++sign;
	sta[++top] = u , insta[u] = 1;
	int siz = V[u].size()-1;
	for(int i=0;i<=siz;i++){
		int t=V[u][i];
		if(!dfn[t]) dfs(t),low[u] = min(low[u],low[t]);
		else if(insta[t] && dfn[t]<low[u]) low[u] = dfn[t];
	}
	if(dfn[u]==low[u]){
		cnt++; do{
			insta[sta[top]] = 0; id[sta[top]] = cnt;
			ww[cnt] += w[sta[top]]; vv[cnt] += v[sta[top]];
		} while(sta[top--]!=u);
	}
}
void DP(int u,int fa){
	siz[u] = ww[u];
	for(int i=ww[u];i<=m;i++) f[u][i] = vv[u];
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(t==fa) continue;
		DP(t,u); siz[u] += siz[t];
		for(int j=m;j>=ww[u];j--) for(int k=0;k<=j-ww[u];k++)
			f[u][j] = max(f[u][j],f[t][k]+f[u][j-k]);
	} 
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<=n;i++) scanf("%d",&v[i]);
	for(int i=1;i<=n;i++){
		int x; scanf("%d",&x); 
		if(x) V[x].push_back(i);
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
	for(int i=1;i<=n;i++){ int siz = V[i].size()-1;
		for(int j=0;j<=siz;j++){ int t=V[i][j]; 
			if(id[t]!=id[i]) du[id[t]]++,add(id[i],id[t]),add(id[t],id[i]);
		}
	} 
	for(int i=1;i<=cnt;i++) if(!du[i]) add(rt,i);
	DP(rt,0); printf("%d",f[rt][min(m,siz[rt])]); return 0;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/85019487