【luogu3387】 【模板】缩点 [tarjan 缩点]
静下心来去看 其实真的很好理解 突然搞不懂我之前为什么死活都看不懂
参悟了学长的代码还有BYVoid的讲解
放一下BYVoid大佬的tarjan伪代码 帮助理解
还有各种变量的含义 (from黄学长
栈里的元素表示的是当前已经访问过但是没有被归类到任一强连通分量的结点
dfn[u] 表示结点 u 在 DFS 中第一次搜索到的次序,通常被叫做时间戳
ow[u] 它表示从 u 或者以 u 为根的子树中的结点,再通过一条反祖边或者横叉边可以到达的时间戳最小的结点 v 的时间戳,并且要求 v 有一些额外的性质:v 还要能够到达 u。
显然通过反祖边到达的结点 v 满足 low 的性质,但是通过横叉边到达的却不一定。
tarjan(u) { DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值 Stack.push(u) // 将节点u压入栈中 for each (u, v) in E // 枚举每一条边 if (v is not visted) // 如果节点v未被访问过 tarjan(v) // 继续向下找 Low[u] = min(Low[u], Low[v]) else if (v in S) // 如果节点v还在栈内 Low[u] = min(Low[u], DFN[v]) if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根 repeat v = S.pop // 将v退栈,为该强连通分量中一个顶点 print v until (u== v) }
倒是看着学长的代码发现我并不理解vector
定义vector<int>G[maxn];G[i][j]表示以点i为起点连出的第j条边在边目录中的编号 图中加入一条边(u,v)则G[u].push_back(v); 遍历点u的邻接点只需遍历u的vector,然后根据u连出的边在边目录中的编号找到终点即可
然后就是后面的那个DAGdp有点点吃力 应该是我没认真理解题的原因
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #include<cmath> #include<vector> #include<stack> #include<algorithm> using namespace std; const int N=100000+5; int n,m,dfn[N],low[N],inst[N],bl[N],idx=0,Bcnt=0; int sum[N],val[N],dp[N]; stack<int>s; vector<int>g[N]; template <class t>void rd(t &x){ x=0;int w=0;char ch=0; while(!isdigit(ch)) w|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); x=w?-x:x; } int head[N],tot=0; struct edge{int u,v,nxt;}e[N]; void add(int u,int v){ e[++tot]=(edge){u,v,head[u]};head[u]=tot; } void tarjan(int u){ dfn[u]=low[u]=++idx; s.push(u);inst[u]=1; for(int i=head[u],v;i;i=e[i].nxt){ v=e[i].v; if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]); else if(inst[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } if(dfn[u]==low[u]){ int v;++Bcnt; do{ v=s.top();s.pop(); sum[Bcnt]+=val[v]; bl[v]=Bcnt; inst[v]=0; }while(v!=u); } } int main(){ rd(n),rd(m); memset(dfn,0,sizeof(dfn)); for(int i=1;i<=n;++i) rd(val[i]); for(int i=1,u,v;i<=m;++i){ rd(u),rd(v); add(u,v); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=tot;++i) if(bl[e[i].u]!=bl[e[i].v]) g[bl[e[i].v]].push_back(bl[e[i].u]); //插入边(bl[v],bl[u]) for(int i=Bcnt;i;--i){ dp[i]=sum[i]; for(int j=0;j<g[i].size();++j) dp[i]=max(dp[i],dp[g[i][j]]+sum[i]); } int ans=0; for(int i=1;i<=Bcnt;++i) ans=max(ans,dp[i]); printf("%d",ans); return 0; }