欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1179
题意概括
有一个有向图,每一个节点有一个权值,其中有一些结束点。
现在,你要从S出发,到达任意一个结束点,使得经过的节点的权值和最大(可以重复经过某一个节点,但是权值只记入一次)。
题解
小码农题。
如果有强连通分量,那么之间的点是可以全部拿到的,傻子才不拿。
所以先Tarjan强连通缩个点。
然后就是一个DAG(有向无环图)了。
那么就是一个记忆化dfs的问题了。
于是就简单了。
but,尴尬的我犯了低级错误,又wa了一次……
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=500000+5; const int Inf=2100000000; struct Gragh{ int cnt,x[N],y[N],nxt[N],fst[N]; void set(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ x[++cnt]=a,y[cnt]=b; nxt[cnt]=fst[a],fst[a]=cnt; } }g,g2; int n,m,time,top,tot; int dfn[N],low[N],bh[N],st[N],w[N],v[N],dp[N],S,P; bool inst[N],vis[N],isp[N],fip[N]; void Tarjan_Prepare(){ time=top=tot=0; memset(bh,0,sizeof bh); memset(st,0,sizeof st); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(vis,0,sizeof vis); memset(inst,0,sizeof inst); } void Tarjan(int x){ dfn[x]=low[x]=++time; inst[x]=vis[x]=1; st[++top]=x; for (int i=g.fst[x];i;i=g.nxt[i]) if (!vis[g.y[i]]){ Tarjan(g.y[i]); low[x]=min(low[x],low[g.y[i]]); } else if (inst[g.y[i]]) low[x]=min(low[x],low[g.y[i]]); if (dfn[x]==low[x]){ tot++; bh[st[top]]=tot; inst[st[top]]=0; while (st[top--]!=x){ bh[st[top]]=tot; inst[st[top]]=0; } } } int dfs(int x){ if (dp[x]!=-Inf) return dp[x]; dp[x]=fip[x]?v[x]:-Inf; for (int i=g2.fst[x];i;i=g2.nxt[i]) dp[x]=max(dp[x],dfs(g2.y[i])+v[x]); return dp[x]; } int main(){ g.set(); scanf("%d%d",&n,&m); for (int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b); g.add(a,b); } for (int i=1;i<=n;i++) scanf("%d",&w[i]); scanf("%d%d",&S,&P); memset(isp,0,sizeof isp); memset(fip,0,sizeof fip); memset(v,0,sizeof v); for (int i=1,pos;i<=P;i++){ scanf("%d",&pos); isp[pos]=1; } Tarjan_Prepare(); for (int i=1;i<=n;i++) if (!vis[i]) Tarjan(i); g2.set(); for (int i=1;i<=n;i++) v[bh[i]]+=w[i],fip[bh[i]]|=isp[i]; for (int i=1;i<=g.cnt;i++) if (bh[g.x[i]]!=bh[g.y[i]]) g2.add(bh[g.x[i]],bh[g.y[i]]); for (int i=1;i<=tot;i++) dp[i]=-Inf; printf("%d",dfs(bh[S])); return 0; }