1. 程式人生 > >[HAOI2010]軟體安裝 [Tarjan + 樹形DP]

[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;
}