HDU 4612 Warm up (dcc缩点+树的直径)

题目链接
在这里插入图片描述
题意:
对于联通图中加入一条边后,图中的最少桥的数量

解题思路:
先把边双联通分量缩出来,形成一颗树,那么树的边就全是桥,现在可以加一条边,消掉尽可能 多的桥,那么直接用树上最远的两个点就可以消掉最多树的直径个桥。

#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define Re  int
using namespace std;
const int N=200010,M=2e6+60000;
int n,m,x,y,Q_o,leaf,ip[N],du[N];
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;
}
struct Tarjan
{
    int o,dfn_o,dfn[N],low[N],head[N],bridge[M<<1];
    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 cle()
    {
        o=1;
        dfn_o=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(head,0,sizeof(head));
        memset(bridge,0,sizeof(bridge));
        Q_o=0;
    }
    inline void tarjan(Re x,Re p)
    {
        dfn[x]=low[x]=++dfn_o;
        for(Re i=head[x],to; i; i=a[i].next)
            if(!dfn[to=a[i].to])
            {
                tarjan(to,i);
                low[x]=min(low[x],low[to]);
                if(low[to]>dfn[x])
                    bridge[i]=bridge[i^1]=1;
            }
            else if(i!=(p^1))
                low[x]=min(low[x],dfn[to]);
    }
    inline void dfs(Re x)
    {
        ip[x]=Q_o;
        for(Re i=head[x]; i; i=a[i].next)
            if(!ip[a[i].to]&&!bridge[i])
                dfs(a[i].to);
    }
    inline void SuoPoint()
    {
        for(Re i=1; i<=n; ++i)
            if(!dfn[i])
                tarjan(i,-1);
        for(Re i=1; i<=n; ++i)
            if(!ip[i])
                ++Q_o,dfs(i);
    }
} T1;
vector<int>G[200001];
int d[N];
int c;
void dfs(int u, int fa)
{
    for (int v : G[u])
    {
        if (v == fa)
            continue;
        d[v] = d[u] + 1;
        if (d[v] > d[c])
            c = v;
        dfs(v, u);
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n<=0&&m<=0)
        {
            break;
        }
        memset(ip,0,sizeof(ip));
        memset(du,0,sizeof(du));
        T1.o=1;
        T1.cle();
        while(m--)
            in(x),in(y),T1.add(x,y),T1.add(y,x);
        T1.SuoPoint();
        leaf=0;

        for(Re i=2; i<=T1.o; i+=2)
            if((x=ip[T1.a[i].to])!=(y=ip[T1.a[i^1].to]))
            {
                G[x].push_back(y);
                G[y].push_back(x);

            }
        dfs(1,0);
        d[c] = 0, dfs(c, 0);
        printf("%d\n", (Q_o-1)-d[c]);
        memset(d,0,sizeof(d));
        for(int i=0; i<=Q_o; i++)
        {
            G[i].clear();
        }

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值