Tarjan缩点后点序即为逆拓扑序
更新日志
update 2024/10/17: 整理格式 update 2024/10/17: 增添例题一道概念
在使用\(Tarjan\)缩点之后,产生的点序,即为逆拓扑序。
思路
很容易感性证明,不难想象一棵dfs生成树
,在缩点之后,上面的点指向下面的点,而由于我们是在dfs
的过程中去给点排序的,因此节点序其实就是dfs序
,故而点序大的必然指向点序小的。
如果原图不只一个入度为0的点,也不会产生影响。
- 如果这点指向另一棵已遍历的树的点,那么点序同样比它大。
- 如果这是一棵独立的树,没有影响。
写法
这并不是一个算法模板,只是一个框架以供参考、促进理解。
void topo(graph &g){
for(int i=scnt;i>=1;i--){//拓扑序遍历
/*SelfOptions*/
for(int e=g.hd[i];e;e=g.ne[e]){
int nxt=g.to[e];
/*SonOptions*/
}
}
}
例题
- 拓扑序:
最大半连通子图
代码
前注:非题解,不做详细讲解
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
typedef pair<int,ll> pil;
typedef pair<ll,ll> pll;
typedef pair<int,pii> pip;
typedef vector<int> veci;
typedef vector<pii> vecp;
typedef priority_queue<int> bghp;
typedef priority_queue<int,vector<int>,greater<int> > lthp;
const int N=1e5+5,M=1e6+5;
int n,m,mod;
struct graph{
int cnt;
int hd[N],ne[M],to[M];
void adde(int u,int v){
to[++cnt]=v;
ne[cnt]=hd[u];
hd[u]=cnt;
}
}ori,aft;
int dcnt;
int dfn[N],low[N];
int scnt;
int scc[N],siz[N];
stack<int> stk;bool ins[N];
void tarjan(int rt){
dfn[rt]=low[rt]=++dcnt;
stk.push(rt);ins[rt]=true;
for(int e=ori.hd[rt];e;e=ori.ne[e]){
int nxt=ori.to[e];
if(!dfn[nxt]){
tarjan(nxt);
low[rt]=min(low[rt],low[nxt]);
}else if(ins[nxt])low[rt]=min(low[rt],dfn[nxt]);
}
if(low[rt]>=dfn[rt]){
++scnt;
while(true){
int now=stk.top();stk.pop();ins[now]=false;
scc[now]=scnt;
siz[scnt]++;
if(now==rt)break;
}
}
}
void rebuild(graph &g1,graph &g2){
for(int i=1;i<=n;i++){
for(int e=g1.hd[i];e;e=g1.ne[e]){
int j=g1.to[e];
if(scc[i]!=scc[j]){
g2.adde(scc[i],scc[j]);
}
}
}
}
int ans1;ll ans2;
int f[N];ll c[N];
int lst[N];
void topo(graph &g){
for(int i=scnt;i>=1;i--){
f[i]=max(f[i],siz[i]);c[i]=max(c[i],1ll);
if(ans1<f[i])ans1=f[i],ans2=c[i];
else if(ans1==f[i])ans2=(ans2+c[i])%mod;
for(int e=g.hd[i];e;e=g.ne[e]){
int nxt=g.to[e];
if(lst[nxt]==i)continue;
lst[nxt]=i;
if(f[nxt]<f[i]+siz[nxt])f[nxt]=f[i]+siz[nxt],c[nxt]=c[i];
else if(f[nxt]==f[i]+siz[nxt])c[nxt]=(c[nxt]+c[i])%mod;
}
}
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>mod;
int u,v;
for(int i=1;i<=m;i++){
cin>>u>>v;
ori.adde(u,v);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
rebuild(ori,aft);
topo(aft);
cout<<ans1<<"\n"<<ans2;
return 0;
}
- 逆拓扑序:
连通数
代码
前注:非题解,不做详细讲解
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
typedef pair<int,ll> pil;
typedef pair<ll,ll> pll;
typedef pair<int,pii> pip;
typedef vector<int> veci;
typedef vector<pii> vecp;
typedef priority_queue<int> bghp;
typedef priority_queue<int,vector<int>,greater<int> > lthp;
const int N=2005,M=4000005;
struct graph{
int cnt;
int hd[N],ne[M],to[M];
void adde(int u,int v){
to[++cnt]=v;
ne[cnt]=hd[u];
hd[u]=cnt;
}
}ori,aft;
int n;
int dcnt;
int dfn[N],low[N];
int scnt;
int scc[N],siz[N];
stack<int> stk;bool ins[N];
void tarjan(graph &g,int rt){
dfn[rt]=low[rt]=++dcnt;
stk.push(rt);ins[rt]=true;
for(int e=g.hd[rt];e;e=g.ne[e]){
int nxt=g.to[e];
if(!dfn[nxt]){
tarjan(g,nxt);
low[rt]=min(low[rt],low[nxt]);
}else if(ins[nxt])low[rt]=min(low[rt],dfn[nxt]);
}
if(low[rt]>=dfn[rt]){
scnt++;
while(1){
int tp=stk.top();stk.pop();ins[tp]=false;
scc[tp]=scnt;
siz[scnt]++;
if(tp==rt)break;
}
}
}
int ans;
int f[N][100];
void topo(graph& g){
for(int i=1;i<=scnt;i++){
f[i][i/30]+=(1<<(i%30));
for(int e=g.hd[i];e;e=g.ne[e]){
int j=g.to[e];
for(int w=0;w<=n/30;w++){
f[i][w]|=f[j][w];
}
}
for(int j=1;j<=scnt;j++){
if((f[i][j/30]>>(j%30))&1)ans+=siz[i]*siz[j];
}
}
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
char c;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>c;
if(c=='1')ori.adde(i,j);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(ori,i);
}
for(int i=1;i<=n;i++){
for(int e=ori.hd[i];e;e=ori.ne[e]){
int j=ori.to[e];
if(scc[i]!=scc[j]){
aft.adde(scc[i],scc[j]);
}
}
}
topo(aft);
cout<<ans;
return 0;
}