JZOJ5465. 【NOIP2017提高A组冲刺11.9】道路重建

Description

小X所居住的X国共有n个城市,有m条无向道路将其连接。作为一个统一的国家,X 城的任意两个城市都可以相互到达。
由于X国正处于地震带上,有时X国中会有至多一条道路发生毁坏,无法使用。如果这条道路的毁坏恰好阻断了某些城市的往来,那么我们称这条道路是危险的。
人们并不喜欢危险的道路,于是人们决定重建恰好一条道路,以减少危险的道路数。请告诉人们,重建恰好一条道路后,危险的道路数最少是多少。

Input

输入文件包含多组数据,对于每一组数据,第一行两个整数n,m,表示X 国的城市数与道路数。
接下来m 行,每行两个整数x,y,描述一条道路。
输入文件以n = 0,m = 0 结尾。
保证输入的图是一张连通图,但不保证不存在重边与自环。

Output

对于每一组数据,输出一行一个整数,表示危险的道路数的最小值。

Sample Input

5 6
1 2
2 1
1 3
2 4
4 4
5 2
0 0

Sample Output

1
样例解释:
一种最优的重建方式是连接城市3,5,这样,只有道路(2,4)是危险的。

数据范围

这里写图片描述

题解

先看看哪一些边是安全的,
就是说它被删掉之后,并不会影响连通性,
也就是环里面的边。

既然是环,就先tarjan缩点。
这样图就变成了树,
用贪心的思想,
如何加一条边,构成树中最大的环,
找直径。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define ll long long
#define N 200003
using namespace std;

char ch;
void read(int& n)
{
    n=0;
    for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
    for(;'0'<=ch && ch<='9';n=(n<<3)+(n<<1)+ch-48,ch=getchar());
}

struct node
{
    int x,y;
}a[N*5];

int n,m,x,y,nxt[N*10],b[N],to[N*10],tot;
int dfn[N],low[N],z[N],f[N],cnt,top,id;
int nxt1[10*N],b1[N],to1[10*N];
int mx,pos;
bool bz[N],w[N];

int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}

void ins(int x,int y)
{
    nxt[++tot]=b[x];
    to[tot]=y;
    b[x]=tot;
}

void ins1(int x,int y)
{
    nxt1[++tot]=b1[x];
    to1[tot]=y;
    b1[x]=tot;
}

void dfs(int x,int len)
{
    bz[x]=0;
    if(len>mx)mx=len,pos=x;
    for(int i=b1[x];i;i=nxt1[i])
        if(bz[to1[i]])dfs(to1[i],len+1);
}

void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++id;
    bz[x]=0;w[x]=1;
    z[++top]=x;

    for(int i=b[x];i;i=nxt[i])
    {
        if(i==(fa^1))continue;
        if(bz[to[i]])
        {
            tarjan(to[i],i);
            low[x]=min(low[x],low[to[i]]);
        }else if(w[x])low[x]=min(low[x],dfn[to[i]]);
    }

    if(low[x]==dfn[x])
    {
        z[top+1]=-1;
        cnt++;
        while(z[top+1]!=x)
        {
            f[z[top]]=cnt;
            w[z[top]]=0;
            top--;
        }
    }
}

bool cmp(node a,node b)
{
    return a.x<b.x || (a.x==b.x && a.y<b.y);
}

int main()
{
    freopen("rebuild.in","r",stdin);
    freopen("rebuild.out","w",stdout);
    read(n);read(m);
    while(n!=0 && m!=0)
    {
        tot=1;memset(b,0,sizeof(b));
        for(int i=1;i<=m;i++)
            read(x),read(y),ins(x,y),ins(y,x);

        memset(bz,1,sizeof(bz));
        memset(w,0,sizeof(w));
        id=cnt=top=0;
        tarjan(1,0);

        tot=0;memset(b1,0,sizeof(b1));
        for(int i=1;i<=n;i++)
            for(int j=b[i];j;j=nxt[j])
                if(f[i]!=f[to[j]])ins1(f[i],f[to[j]]);

        mx=0;memset(bz,1,sizeof(bz));
        dfs(1,0);
        mx=0;memset(bz,1,sizeof(bz));
        dfs(pos,0);

        printf("%d\n",cnt-mx-1);

        read(n);read(m);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值