Codeforces 1547 G. How Many Paths? —— Tarjan+dfs

2 篇文章 0 订阅

This way

题意:

现在有一张有向图,从1开始遍历,问你点i的值是:
0(到达不了)
1(只有一条路从1到i)
2(有大于等于两条且从1到i的路径数量有限)
-1(从1到i的路径数量无限)

题解:

首先可以dfs一遍整张图,每个点最多走两次,这样就能将0,1,2三种情况求出来。
对于-1的情况,我们知道如果x是-1当且仅当它在1能够到达的环上或者1可以经过一个环到达x。那么考虑用tarjan求出强联通分量,然后缩点。
称一个scc是有效的如果这个scc的大小>=2,或者这个点有自环。
接下来dfs查看每个scc是否是有效的且1能够到达,或者1能够经过一个有效的scc到达它。

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
struct node{
    int to,next;
}e[N];
int cnt,head[N];
void add(int x,int y){
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
// scc
int low[N],dfn[N],scc[N],tim,num;
stack<int>st;
bool in[N];
// init
void init(int siz) {
    memset(head,-1,sizeof(int)*(siz+1));
    cnt=tim=num=0;
    memset(dfn,0,sizeof(int)*(siz+1));
    memset(in,0,sizeof(bool)*(siz+1));
    while(!st.empty())
        st.pop();
}
// deal
void tarjan(int x) {
    low[x]=dfn[x]=++tim;
    st.push(x);
    in[x]=1;
    for(int i=head[x];~i;i=e[i].next) {
        int ne=e[i].to;
        if(!dfn[ne])
            tarjan(ne),low[x]=min(low[x],low[ne]);
        else if(in[ne])
            low[x]=min(low[x],dfn[ne]);
    }
    if(low[x]==dfn[x]) {
        ++num;
        while(1) {
            int v=st.top();
            st.pop();
            scc[v]=num;
            in[v]=0;
            if(x==v)
                break;
        }
    }
}
vector<int>vec[N];
int ans[N],sum[N],can[N];
void dfs1(int x,int fa){
    can[x]++;
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(ne==fa)continue;
        if(can[ne]==2)continue;
        dfs1(ne,x);
    }
}
void dfs2(int x,int fa,int f){
    if(f||sum[x]>=2)ans[x]=-1;
    else ans[x]=1;
    for(int i:vec[x]){
        if(i==fa||ans[i]==-1)continue;
        if(ans[i]==1&&ans[x]!=-1)continue;
        dfs2(i,x,f||sum[x]>=2);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        init(n);
        int x,y;
        while(m--){
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        tarjan(1);
        for(int i=1;i<=n;i++){
            sum[scc[i]]++;
            for(int j=head[i];~j;j=e[j].next){
                int ne=e[j].to;
                if(ne==i)sum[scc[i]]=2;
                else vec[scc[i]].push_back(scc[ne]);
            }
        }
        dfs1(1,0);
        dfs2(scc[1],0,0);
        for(int i=1;i<=n;i++){
            if(ans[scc[i]]==-1)printf("-1%c",i==n?'\n':' ');
            else printf("%d%c",can[i],i==n?'\n':' ');
        }
        for(int i=1;i<=n;i++)vec[i].clear(),sum[i]=ans[i]=can[i]=scc[i]=0;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值