HDU3394 Railway(Tarjan+dcc缩点)

题目链接HDU3394
在这里插入图片描述
题目大意:
N个点,M条即将要修建的铁路。现在问,为了环形游览N个点。问我们有多少条路径是冲突的。定义冲突路径为:它不仅仅包含于一个环里面。如果一条路径不属于任何一个环,那么没有必要修建它。询问我们不必要修建的铁路数和冲突的铁路数。

分析:
显然不必要修建的铁路数就是该无向图中桥的数目。为什么?
对于一个环中的边,我们移除任意一条边,任然可以实现环中节点可达性。如果一个图移除掉了桥,其联通分量会增加。即桥不包含于任何一个环,因此没必要修建为桥(割边)的铁路。

而对于冲突边,其必然在点双连通分量中出现,如果一个块的点数等于边数,说明其是一个环,没有冲突边,但如果边数大于点数,说明这个块里至少有两个环,简单分析知此时块内所有边均为冲突边,所以问题转变为求桥的数量以及每个块的点数和边数,用Tarjan求点双连通分量,顺便就可以求出桥数,每找到一个块,就统计块内点的数量以及这些点之间连的边数,判断边数是否大于点数,如果大于则将边数累加到答案中

#include<bits/stdc++.h>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#define LL long long
#define Re register int
using namespace std;
const int N=11003,M=101000;
int n,m,x,y,T,Q_o,tmp,ans1,cut[N],bridge;LL ans2;//cut标记割点
std::vector<int>dcc[N];//缩点后第i个块存的原先的节点编号
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int flag[N];
inline int max(Re a,Re b){return a>b?a:b;}

struct Tarjan{
    int o,t,dfn_o,Q[N],dfn[N],low[N],head[N];
    struct QAQ{int to,next;}a[M<<1];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    inline void CL(){
        memset(head,0,sizeof(head));
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(cut,0,sizeof(cut));
        dfn_o=t=o=0;
    }
    inline void Sum(Re x){

        memset(flag,0,sizeof(flag));
        int rnm=0;
        for(int j=0;j<dcc[x].size();j++){
            flag[dcc[x][j]]=1;
        }
        for(int j=0;j<dcc[x].size();j++){

        for(int k=head[dcc[x][j]];k;k=a[k].next){
          if(flag[a[k].to])rnm++;

        }


        }
        rnm=rnm/2;
        if(rnm>dcc[x].size())ans1+=rnm;

}
    inline void tarjan(Re x,Re rt){
        Re son=0;Q[++t]=x,dfn[x]=low[x]=++dfn_o;
        for(Re i=head[x],to;i;i=a[i].next){
           if(a[i].to==rt)continue;
            if(!dfn[to=a[i].to]){
                tarjan(to,x),++son;
                low[x]=min(low[x],low[to]);

                if(low[to]>=dfn[x]){
                        if(low[to]>dfn[x])bridge++;
                    if(x!=rt)cut[x]=1;
                    else cut[x]|=son>1;
                    ++Q_o;
                    while(1){
                        dcc[Q_o].push_back(Q[t]);
                        if(to==Q[t--])break;
                    }
                    dcc[Q_o].push_back(x);
                    Sum(Q_o);
                }
            }
            else low[x]=min(low[x],dfn[to]);
        }
    }
    inline void SuoPoint(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,i);}
}T1;


int main(){
    while(scanf("%d%d",&n,&m)){
            if(n<=0&&m<=0){break;}
            bridge=0;
            ans1=0;
        for(Re i=1;i<=Q_o;++i)dcc[i].clear();
        T1.CL(),Q_o=ans1=0;
        while(m--){in(x),in(y);T1.add(x,y),T1.add(y,x),n=max(n,max(x,y));}
        T1.SuoPoint();
        printf("%d %d\n",bridge,ans1);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值