luogu_3387【题解】缩点 tarjan
题目:https://www.luogu.org/problemnew/show/P3387
如题目描述,缩点加DP
怎么缩点????
tarjan算法,主要在于维护了一个low[ ] 数组。
low 数组表示一个点可以到达的最浅的点的dfs序。
如果一个点的low[ ]数组与自己的dfs序相等,证明自己整个子树是强连通分量。
强连通分量就可以缩成一个点。
tarjan代码如下(来源:洛谷网校课件)
int s[MAXN], stop; int dfn[MAXN], low[MAXN]; int scccnt, sccnum[MAXN]; int dfscnt; inline void tarjan(int now){ dfn[now] = low[now] = ++dfscnt; s[stop++] = now; for (int i = he[now]; i != 0 ; i = ne[i]){ if (!dfn[ed[i]]) { tarjan(ed[i]); low[now] = min(low[now], low[ed[i]]); } else if(!sccnum[ed[i]]) { low[now] = min(low[now], dfn[ed[i]]); } } if (dfn[now] == low[now]) { scccnt++; do { sccnum[s[--stop]] = scccnt; } while(s[stop] != now); } }
这个代码中,sccnum[ ]存的就是缩后的点的顺序。(重新排列了。)
下个代码则是,sccnum[ ]存的缩之前强连通分量的最浅的点的编号,也就是now。
代码如下。
int s[maxn], stop; int dfn[maxn],low[maxn],dfscnt,sccnum[maxn]; inline void tarjan(int now){ dfn[now]=low[now]=++dfscnt; s[stop++]=now; for(int i=head[now];i;i=nxt[i]){ int t=to[i]; if(!dfn[t]){ tarjan(t); low[now]=min(low[now],low[t]); }else if(!sccnum[t]){ low[now]=min(low[now],dfn[t]); } } if(dfn[now]==low[now]){ do{ int t=s[--stop]; sccnum[t]=now; //主要区别。 if(now!=t) d[now]+=d[t]; }while(s[stop]!=now); } }
那么这道题也就可以解决了。
DP则简单。
就是重新在缩点之后连边,从子节点中更新。
代码如下。
#include<bits/stdc++.h> #define sc(x) scanf("%d",&x) using namespace std; const int maxn=1e4+10,maxm=1e5+10; int n,m,tot; int d[maxn],head[maxm],nxt[maxm<<1],to[maxm<<1],from[maxm<<1]; inline void add(int u,int v){ to[++tot]=v,from[tot]=u; nxt[tot]=head[u],head[u]=tot; } int s[maxn], stop; int dfn[maxn],low[maxn],dfscnt,sccnum[maxn]; inline void tarjan(int now){ dfn[now]=low[now]=++dfscnt; s[stop++]=now; for(int i=head[now];i;i=nxt[i]){ int t=to[i]; if(!dfn[t]){ tarjan(t); low[now]=min(low[now],low[t]); }else if(!sccnum[t]){ low[now]=min(low[now],dfn[t]); } } if(dfn[now]==low[now]){ do{ int t=s[--stop]; sccnum[t]=now; if(now!=t) d[now]+=d[t]; }while(s[stop]!=now); } } struct node{ int from,to,nxt; }e[maxm<<1]; int he[maxm],cnt,du[maxn],ans,dis[maxn]; inline void topo() { queue<int> q; for(int i=1;i<=n;i++) if(sccnum[i]==i&&!du[i]){ q.push(i);dis[i]=d[i]; } while(!q.empty()){ int k=q.front();q.pop(); for(int i=he[k];i;i=e[i].nxt){ int t=e[i].to; dis[t]=max(dis[t],dis[k]+d[t]); if(!--du[t]) q.push(t); } } for(int i=1;i<=n;i++) ans=max(ans,dis[i]); } int main() { sc(n),sc(m); for(int i=1;i<=n;i++) sc(d[i]); for(int i=1;i<=m;i++){ int u,v; sc(u),sc(v); add(u,v); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=m;i++){ int u=sccnum[from[i]],v=sccnum[to[i]]; if(u!=v){ e[++cnt].from=u; e[cnt].to=v; e[cnt].nxt=he[u]; he[u]=cnt; du[v]++; } } topo(); printf("%d\n",ans); system("pause"); return 0; }