Codeforces 999E(强连通分量缩点)

传送门

题面:

 

E. Reachability from the Capital

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

There are nn cities and mm roads in Berland. Each road connects a pair of cities. The roads in Berland are one-way.

What is the minimum number of new roads that need to be built to make all the cities reachable from the capital?

New roads will also be one-way.

Input

The first line of input consists of three integers nn, mm and ss (1≤n≤5000,0≤m≤5000,1≤s≤n1≤n≤5000,0≤m≤5000,1≤s≤n) — the number of cities, the number of roads and the index of the capital. Cities are indexed from 11 to nn.

The following mm lines contain roads: road ii is given as a pair of cities uiui, vivi (1≤ui,vi≤n1≤ui,vi≤n, ui≠viui≠vi). For each pair of cities (u,v)(u,v), there can be at most one road from uu to vv. Roads in opposite directions between a pair of cities are allowed (i.e. from uu to vv and from vv to uu).

Output

Print one integer — the minimum number of extra roads needed to make all the cities reachable from city ss. If all the cities are already reachable from ss, print 0.

Examples

input

Copy

9 9 1
1 2
1 3
2 3
1 5
5 6
6 1
1 8
9 8
7 1

output

Copy

3

input

Copy

5 4 5
1 2
2 3
3 4
4 1

output

Copy

1

Note

The first example is illustrated by the following:

For example, you can add roads (6,46,4), (7,97,9), (1,71,7) to make all the cities reachable from s=1s=1.

The second example is illustrated by the following:

In this example, you can add any one of the roads (5,15,1), (5,25,2), (5,35,3), (5,45,4) to make all the cities reachable from s=5s=5.

题目描述:

    给你n个点,m条边,以及一个初始点s,问你至少还需要增加多少条边,使得初始点s与剩下其他的所有点都连通。

题目分析:

    因为要讨论连通性,而题目所要讨论的有向图,因此我们可以首先统计强连通分量,并通过Tarjin缩点,并重新构图。将图重构之后,我们会发现,倘若除了s结点所在的连通分量,如果其他连通分量所构成的新点的入度为0,则使这个连通分量与s连通的最优的方案是将这个点与s结点相连。

    因此,对于这个问题,我们只需要在缩点之后统计一下入读为0的新点即是答案。

代码:

 

#include <bits/stdc++.h>
#define maxn 200005
using namespace std;
struct edge{
    int next,to;
}q[maxn];
int head[maxn],dfn[maxn],low[maxn],cnt,tot;
int vis[maxn],belong[maxn],index,belong_num[maxn],num_index;
int indegree[maxn],outdegree[maxn];
void add_edge(int from,int to){
    q[cnt].next=head[from];
    q[cnt].to=to;
    head[from]=cnt++;
}
void init(){//初始化
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(head,-1,sizeof(head));
    memset(low,0,sizeof(low));
    memset(belong_num,0,sizeof(belong_num));//在某个连通块中有多少个结点
    memset(indegree,0,sizeof(indegree));//新图的入度
    memset(outdegree,0,sizeof(outdegree));
    index=num_index=cnt=tot=0;
}
stack<int>st;
void tarjin(int x){//Tarjin的主体
    dfn[x]=low[x]=++tot;
    vis[x]=1;
    st.push(x);
    for(int i=head[x];i!=-1;i=q[i].next){
        edge e=q[i];
        if(!dfn[e.to]){
            tarjin(e.to);
            low[x]=min(low[e.to],low[x]);
        }
        else if(vis[e.to]==1){
            low[x]=min(low[x],dfn[e.to]);
        }
    }
    if(dfn[x]==low[x]){
        int v;
        index=index+1;
        do{
            v=st.top();
            st.pop();
            belong[v]=index;
            belong_num[index]++;
            vis[v]=0;
        }while(v!=x);
    }
}
void solve(int n,int m,int root){
    for(int i=1;i<=n;i++){//对图进行Tarjin
        if(!dfn[i]){
            tarjin(i);
        }
    }
    //如果连通分量只有一个,则直接输出0
    if(index==1){
        puts("0");
        return ;
    }
    indegree[belong[root]]=1;//确保初始点root所在的连通分量入度不为0
    
    for(int i=1;i<=n;i++){//重构图的过程
        for(int j=head[i];j!=-1;j=q[j].next){
            edge e=q[j];
            if(belong[i]==belong[e.to]) continue;
            indegree[belong[e.to]]++;
            outdegree[belong[i]]++;
        }
    }
    int cnt=0;//统计入度为0的点
    for(int i=1;i<=index;i++){
        if(indegree[i]==0){
            cnt++;
        }
    }
    cout<<cnt<<endl;
}
int main()
{
    int n,m,s;
    cin>>n>>m>>s;
    init();
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add_edge(a,b);
    }
    solve(n,m,s);
    return 0;
}

 

转载于:https://www.cnblogs.com/Chen-Jr/p/11007272.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值