题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2427
今天的考试题……没有想到环的情况。
希望自己能打出正确的tarjan缩点板子……
main函数里还有各种需要注意的地方。比如tarjan要从rd为0的点或者环上的任意一点进入。不过bzoj好像不判rd为0,只看dfn也行。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=105,M=505; int n,m,tv[N],tw[N],v[N],w[N],head[N],txnt,xnt,dp[N][M]; int cnt,col[N],stack[N],top,siz[N],ans,dfn[N],low[N],tim; bool ins[N],rd[N]; struct Ed{ int next,fr,to;Ed(int n=0,int f=0,int t=0):next(n),fr(f),to(t) {} }ed[M<<1]; void tarjan(int cr) { stack[++top]=cr;ins[cr]=1; dfn[cr]=low[cr]=++tim; for(int i=head[cr],to;i;i=ed[i].next) if(ins[to=ed[i].to])low[cr]=min(low[cr],dfn[to]); else if(!dfn[to])tarjan(to),low[cr]=min(low[cr],low[to]);//!dfn[to]才进入!!! if(low[cr]==dfn[cr]) //这样才... { cnt++; while(stack[top]!=cr) { int k=stack[top--]; tv[cnt]+=v[k];tw[cnt]+=w[k];col[k]=cnt;ins[k]=0; } col[cr]=cnt;ins[cr]=0;top--; tv[cnt]+=v[cr];tw[cnt]+=w[cr];//别忘了 } } void dfs(int cr) { siz[cr]=tv[cr];dp[cr][tv[cr]]=tw[cr]; for(int i=head[cr],to;i;i=ed[i].next) { dfs(to=ed[i].to); for(int j=min(m,siz[cr]+siz[to]);j>tv[cr];j--) for(int k=max(0,j-siz[cr]);k<=siz[to]&&k<=(j-tv[cr]);k++) dp[cr][j]=max(dp[cr][j],dp[to][k]+dp[cr][j-k]); siz[cr]+=siz[to]; } } int main() { // freopen("software.in","r",stdin); // freopen("software.out","w",stdout); scanf("%d%d",&n,&m);int x; for(int i=1;i<=n;i++)scanf("%d",&v[i]); for(int i=1;i<=n;i++)scanf("%d",&w[i]); for(int i=1;i<=n;i++) { scanf("%d",&x);if(!x)continue; ed[++xnt]=Ed(head[x],x,i);head[x]=xnt;rd[i]++; } for(int i=1;i<=n;i++)if(!rd[i])tarjan(i); for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);//环 memset(head,0,sizeof head);memset(rd,0,sizeof rd); for(int i=1,u,v;i<=xnt;i++) if((u=col[ed[i].fr])!=(v=col[ed[i].to])) ed[++txnt]=Ed(head[u],u,v),head[u]=txnt,rd[v]=1; for(int i=1;i<=cnt;i++)if(!rd[i])ed[++txnt]=Ed(head[0],0,i),head[0]=txnt; dfs(0); for(int i=0;i<=m;i++)ans=max(ans,dp[0][i]); printf("%d",ans); return 0; }