[HAOI2010]軟體安裝 [Tarjan + 樹形DP]
阿新 • • 發佈:2018-12-16
我們先Tarjan 縮點 , 然後樹形揹包
我們令f[u][i] 表示點u及兒子選i個的最大價值
我們發現如果要加上兒子的貢獻 , 當前點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; }