D15【模板】SCC 缩点 Tarjan 算法
D15【模板】SCC 缩点 Tarjan 算法_哔哩哔哩_bilibili
参考:D14【模板】强连通分量 Tarjan 算法 - 董晓 - 博客园
图的问题注意:有向无向?有环无环?连通不连通?正权负权?
对有向有环图,把每个强连通分量缩成一个点
这张图会变成一个 DAG,可以进行拓扑排序以及更多其他操作
题目1: P2812 校园网络【[USACO]Network of Schools加强版】 - 洛谷
1. SCC缩点
2. 统计缩点的入度、出度
3. 构造答案
// SCC 缩点 Tarjan 算法 O(N) #include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,a,b; vector<int> e[N]; int dfn[N],low[N],stk[N],top,scc[N],cnt; int in[N],out[N]; //SCC的入度,出度 void tarjan(int x){ dfn[x]=low[x]=++dfn[0]; stk[++top]=x; for(int y : e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(stk[top+1]!=x) scc[stk[top--]]=cnt; } } int main(){ cin>>n; for(int i=1,a; i<=n; i++) while(cin>>a,a) e[i].push_back(a); for(int i=1; i<=n; i++)if(!dfn[i]) tarjan(i); for(int x=1; x<=n; x++)for(int y:e[x]) if(scc[x]!=scc[y]) in[scc[y]]++,out[scc[x]]++; int a=0,b=0; for(int i=1; i<=cnt; i++){ if(!in[i]) a++; if(!out[i]) b++; } printf("%d\n",a); printf("%d",cnt==1?0:max(a,b)); }
题目2: P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷
1. 缩点
2. 统计缩点的出度
3. 如果出度为 0 的缩点个数 $>1$,那么全明星牛数 $=0$
如果出度为 0 的缩点个数 $=1$,那么全明星牛数 $=$ 出度为 0 的缩点包含的点数
#include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,a,b; vector<int> e[N]; int dfn[N],low[N],stk[N],top,scc[N],siz[N],cnt; int out[N]; //SCC的出度 void tarjan(int x){ dfn[x]=low[x]=++dfn[0]; stk[++top]=x; for(int y:e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(stk[top+1]!=x) scc[stk[top--]]=cnt, ++siz[cnt]; } } int main(){ cin>>n>>m; while(m--) cin>>a>>b,e[a].push_back(b); for(int i=1; i<=n; i++)if(!dfn[i]) tarjan(i); for(int x=1; x<=n; x++)for(int y : e[x]) if(scc[x]!=scc[y]) ++out[scc[x]]; int sum=0,zeros=0; for(int i=1; i<=cnt; i++)if(out[i]==0){ sum=siz[i]; ++zeros; //出度为0的SCC的个数 } if(zeros>1) sum=0; cout<<sum<<endl; }
题目3: P3387 【模板】缩点 - 洛谷
1. 缩点
2. 对缩点建拓扑图(注意编号逆序)
3. 对拓扑图求最长路
#include<bits/stdc++.h> using namespace std; const int N=100010; vector<int> e[N],ne[N]; int n,m; int dfn[N],low[N],stk[N],top,scc[N],cnt; int w[N],nw[N],d[N]; void tarjan(int x){ dfn[x]=low[x]=++dfn[0]; stk[++top]=x; for(int y : e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(stk[top+1]!=x) scc[stk[top--]]=cnt; } } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]; for(int i=1,a,b;i<=m;i++) cin>>a>>b,e[a].push_back(b); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); //SCC缩点 for(int x=1;x<=n;x++){ //枚举原始点 nw[scc[x]]+=w[x]; //累加缩点的点权 for(int y:e[x])if(scc[x]!=scc[y]) //不在同一个缩点 ne[scc[x]].push_back(scc[y]); //缩点之间连边 } for(int x=cnt;x;x--){ //枚举缩点(缩点编号是拓扑逆序的) if(d[x]==0) d[x]=nw[x]; //起点 for(int y:ne[x]) d[y]=max(d[y],d[x]+nw[y]); //更新最长路 } cout<<*max_element(d+1,d+cnt+1); }
浙公网安备 33010602011771号