缩点tarjan练习

注意:

tarjan缩点,对于有向、无向图依然成立。因为是min操作,所以是不断更新的。

tarjan缩点,对于dp树的操作,从孩子结点贡献往上增加,这样是不对的:

如图,不是环所以不会被删除,但是如果dp树通过拓扑排序实现的话,3的结点被2记录之后,又通过1再次被记录。

缩点后的本来就不一定是树的(一般都是有向图,如果是无向图肯定还是树)

 

T1:洛谷P2341

求被所有点都能到达的点有多少个。

题解:首先考虑到有环、对于环、环内所有点都是互相指向的,所以可以缩点看成一个点。对于这个点的贡献是强连通分量里的个数。答案要求的点显然是没有出边的,即不指向别的点。所以记录建新图后的出度判断0即可。

但是由于新图可能有多个完全分开的子图,那样会有多个出度为0的点,或者本身的图就有多个出度为0的点。那样一定没有答案所需要的点。特判即可。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;

const int maxm = 100005;
const int maxv = 20005;
struct Edge{
    int from,to,dist;
    Edge(){}
    Edge(int _from,int _to,int _dist):from(_from),to(_to),dist(_dist){}
};

int n,m;
int etop=1,dfscnt,scccnt;
int he[maxv],ne[maxm];
int sccnum[maxv],low[maxv],dfn[maxv];
int L[maxm],R[maxm];
int dp[maxv],ind[maxv];
stack<int>st;
Edge ed[maxm];

void insert(int u,int v,int w){
    ed[etop]=Edge(u,v,w);
    ne[etop]=he[u];
    he[u]=etop++;	
}

void tarjan(int now){
    st.push(now);
    dfn[now]=low[now]=++dfscnt;
    for(int i=he[now];i;i=ne[i]){
        Edge &e=ed[i];
        if(!dfn[e.to]){
            tarjan(e.to);
            low[now]=min(low[now],low[e.to]);
        }else if(!sccnum[e.to]){
            low[now]=min(low[now],dfn[e.to]);
        }
    }
    if(low[now]==dfn[now]){
        scccnt++;
        int x;
        do{
            x=st.top();st.pop();
            sccnum[x]=scccnt;
            dp[scccnt]++;
        }while(x!=now);
    }
}

int main(){
    cin>>n>>m;
    FOR(i,1,m){
        int x,y;
        scanf("%d%d",&x,&y);
        insert(x,y,0);
        L[i]=x,R[i]=y;
    }	
    FOR(i,1,n)if(!sccnum[i])tarjan(i);
    etop=1;
    memset(he,0,sizeof(he));
    memset(ne,0,sizeof(ne));
    FOR(i,1,m){
        int x=sccnum[L[i]],y=sccnum[R[i]];
        if(x!=y){
            insert(x,y,0);
            ind[x]++;
        }
    }
    int ans=0;
    FOR(i,1,scccnt)if(!ind[i]){
        if(ans){
            ans=0;
            break;
        }
        else ans=dp[i];
    }
    cout<<ans<<endl;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值