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*/
        }
    }
}

例题

  1. 拓扑序:
    最大半连通子图
代码

前注:非题解,不做详细讲解

#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;
}
  1. 逆拓扑序:
    连通数
代码

前注:非题解,不做详细讲解

#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;
}

posted @ 2024-10-17 07:55  HarlemBlog  阅读(7)  评论(0编辑  收藏  举报