[HAOI2010]软件安装
如果没有环的话其实就是一个比较水的树形背包。但是现在有环,并且当我们选择环中任何一点时其他环上的点也都要被选择,然后就可以缩点解决环的问题。
然后缩点后会形成一个森林,我们建虚点$0$连向各自联通块的根,然后做一个树上背包即可
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1001; struct node{ int u,v,nex; }x[MAXN<<1]; struct node1{ int u,v; }E[MAXN]; int dfn[MAXN],uu[MAXN<<1],CNT,low[MAXN],cnt,n,w,num,col[MAXN],co,head[MAXN],v[MAXN],W[MAXN]; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int sta[MAXN],tot; int ww[MAXN],vv[MAXN]; void tarjan(int f,int fath){ dfn[f]=low[f]=++num;sta[++tot]=f; for(int i=head[f];i!=-1;i=x[i].nex){ int v=x[i].v; if(!dfn[v]) tarjan(v,f),low[f]=min(low[f],low[v]); else if(!col[v]) low[f]=min(low[f],dfn[v]); } if(low[f]==dfn[f]){ col[f]=++co; while(sta[tot]!=f){ col[sta[tot]]=co; ww[co]+=W[sta[tot]],vv[co]+=v[sta[tot]]; tot--; } ww[co]+=W[sta[tot]],vv[co]+=v[sta[tot]]; tot--; }return; } int f[MAXN]; bool cmp(node1 x1,node1 x2){ if(x1.u==x2.u) return x1.v<x2.v; return x1.u<x2.u; } int find(int xx){ if(f[xx]==xx) return xx; return f[xx]=find(f[xx]); } void merge(int x1,int x2){ int t1=find(x1),t2=find(x2); f[t2]=t1; return; } int group[MAXN][MAXN],size[MAXN]; void dfs(int f){ size[f]=1; for(int i=head[f];i!=-1;i=x[i].nex){ dfs(x[i].v); size[f]+=size[x[i].v]; }return; } int _MAXN[MAXN],root[MAXN]; void build(){ for(int i=1;i<=co;i++) f[i]=i; for(int i=1;i<=CNT;i++) E[i].u=col[E[i].u],E[i].v=col[E[i].v]; sort(E+1,E+CNT+1,cmp); memset(head,-1,sizeof(head)),cnt=0; for(int i=1;i<=CNT;i++){ if((E[i].u!=E[i].v)&&(E[i].u!=E[i-1].u||E[i].v!=E[i-1].v)){ merge(E[i].u,E[i].v); add(E[i].u,E[i].v); } } memset(root,-1,sizeof(root)); for(int i=1;i<=co;i++) f[i]=find(f[i]); for(int i=1;i<=co;i++){ memset(size,0,sizeof(size)); dfs(i); if(size[i]>_MAXN[f[i]]){ _MAXN[f[i]]=size[i]; root[f[i]]=i; } } for(int i=1;i<=co;i++) if(root[i]!=-1)add(0,i); ww[0]=1,vv[0]=0; w++; } int dp[MAXN][MAXN]; void dp_tree(int f,int fath){ for(int k=head[f];k!=-1;k=x[k].nex){ int v=x[k].v; dp_tree(v,f); for(int i=w;i>=0;i--){ for(int j=i;j>=0;j--) if(i-j>=ww[v]) { dp[f][i]=max(dp[f][i],dp[f][j]+dp[v][i-j]); } } } for(int i=w;i>=ww[f];i--) dp[f][i]=dp[f][i-ww[f]]+vv[f]; } int main(){ memset(head,-1,sizeof(head)); n=read(),w=read(); for(int i=1;i<=n;i++) W[i]=read(); for(int i=1;i<=n;i++) v[i]=read(); for(int i=1;i<=n;i++){ int u=read(); if(u) { E[++CNT].u=u,E[CNT].v=i; add(u,i); } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0); build(); dp_tree(0,0); cout<<dp[0][w]; }