C++题解:公路

目录

题目 

输入格式

输出格式

题解:



题目 

  • 1000ms
  • 131072K

蒜头君有一些蒜园子,这些园子是由公路相互连通,其中一些形成回路的参观路线。如果有一条公路被多条参观路线公用,那么就可能会发生冲突;如果一条公路没有在任何一个回路内,那么就不会发生冲突。

简单来说,就是在一个无向图中。如果至少有两个环共用了一些边,那么这些边被认为是“冲突边”。如果一些边不在任何一个环中,这些边被认为是“多余边”。请问有多少条公路有冲突,多少条公路没有冲突,另外这图不一定是连通的。

输入格式

第一行包含两个整数 n(0<n≤10000),m(0≤m≤100000) ,表示蒜园的数量和公路的数量。

接下来有 m 行,每行包含两个整数 u,v(0≤u,v<n) 表示蒜园 u 和蒜园 v 之间有一条公路。

你可以假设这里没有自环没有重边。

输出格式

输出以空格隔开的两个整数,分别表示没有冲突公路数目和有冲突公路的数目。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入

8 10
0 1
1 2
2 3
3 0
3 4
4 5
5 6
6 7
7 4
5 7

样例输出

1 5



题解:

知识点:tarjan算法、环

分析:

这里我们应该拿出点双,而不是边双来求。

做完点双后,我们来看每一个点双连通分量:

  1. 当点数 = 边数,形成一个环,什么都不做。

  2. 当点数 > 边数(一条线段,说明这条边是桥),加到 ans1(第一个问题) 上去。

  3. 当点数 < 边数,那么就含 1 个以上的环了。这个时候点双内部所有点都加到 ans2(第二个问题) 上去。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
#include<set>
#define _for(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;
const int N=1e4+5,M=2e5+5;
struct node{
    int u,v;
    node(){}
    node(int _u,int _v){
        u=_u;
        v=_v;
    }
};
int n,m;
int idx,h[N],e[M],ne[M];
int times=0;
int dfn[N],low[N];
int bcc_cnt=0;
stack<node> S;
int ans1,ans2;
bool vis[N];
void c_plus(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
void init(){
    idx=0;
    memset(h,-1,sizeof(h));
}
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
void tarjan(int u,int fa){
    vis[u]=true;
    dfn[u]=low[u]=++times;
    //S.push(node(fa,u));
    for (int i=h[u];~i;i=ne[i]){
        int v=e[i];
        if (dfn[v]==0){
            S.push(node(u,v));
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if (low[v]>dfn[u]){
                ans1++;
            }
            if (low[v]>=dfn[u]){
                ++bcc_cnt;
                set<int> point;
                int sum=0;
                while (true){
                    node x=S.top();
                    point.insert(x.u);
                    point.insert(x.v);
                    S.pop();
                    if (x.u==u && x.v==v){
                        break;
                    }
                }
                for (auto x: point){//求出该点双的边的数量
                    for (int i=h[x];~i;i=ne[i]){
                        int j=e[i];
                        if (j!=x && point.count(j)){
                            sum++;
                        }
                    }
                }
                sum>>=1;
                if (sum>point.size()){
                    ans2+=sum;
                }
            }
        }else if (dfn[v]<dfn[u] && v!=fa){
            S.push(node(u,v));
            low[u]=min(low[u],dfn[v]);
        }
    }
}
int main(){
    c_plus();
    init();
    cin>>n>>m;
    _for(i,0,m){
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    _for(i,0,n){
        if (!vis[i]){
            tarjan(i,-1);
        }
    }
    cout<<ans1<<' '<<ans2<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值