hdu 4612 Warm up (边BCC缩点、带重边)

题目大意:有重边的无向图,问添加一条边最多能够减少多少个桥。


求出边-连通分量,缩点后得到一棵树。在该树中,所有的边都是桥,找LCA最远的两个叶子节点(就是树的直径),其所含的桥个数最多。因此连接该两个叶子节点可使得桥的个数减少最多。这是基本的算法思想。


然后的问题是如何处理重边?

”一般的简单无向图,tarjan求桥的dfs有两个参数x和fa,其中参数fa就是用于判定某边(x,t)是不是刚刚走过来的边.通常是用fa标记父节点。
在有重边的情形下,这样判就不太方便了。因为标记父节点之后就可能无法访问从fa出发的另一条重边了。
于是我们修改这个判定条件,把参数fa改为刚刚走过来的这条边的编号.
这就没问题了.至于具体的实现,由于一条无向边拆成两条有向边存下来的时候,编号是连续的,因此像类似4,5这样的两条边,可以通过判断4/2==5/2来判定是否是由同一条无向边拆出来的“



再一个就是如何求树的直径?相关证明


最后还有个问题:数据量很大。很容易爆栈。得加上这句

#pragma comment(linker,"/STACK:1024000000,1024000000")

然后用C++提交。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;
#define N 200005
struct Edge{
    int v,next;
}edge[2000010];
int pre[N],S[N],top,low[N],head[N],cnt,dfs_clock,bcc_cnt,bccno[N],bridge;

inline void add(int u,int v){
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int fa){
    pre[u]=low[u]=++dfs_clock;
    S[top++]=u;
    for(int i=head[u];~i;i=edge[i].next){
        if(i==(fa^1)) continue;
        int v=edge[i].v;
        if(!pre[v]){
            dfs(v,i);
            low[u]=min(low[u],low[v]);
        }
        else if(!bccno[v]) low[u]=min(low[u],pre[v]);
    }
    if(low[u]==pre[u]){
        ++bcc_cnt;
        for(;;){
            int x=S[--top];
            bccno[x]=bcc_cnt;
            if(x==u) break;
        }
    }
}

inline void find_bcc(int n){
    memset(pre,0,sizeof(pre));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=bridge=top=0;
    for(int i=0;i<n;++i) if(!pre[i]) dfs(i,-1);
}

vector<int>G[N];
int d[N];
bool vis[N];
queue<int>q;
int bfs(int s,int &t){
    memset(d,0,sizeof(d));
    memset(vis,0,sizeof(vis));
    t=s;
    int ans=0;
    q.push(s);
    vis[s]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<G[u].size();++i){
            int v=G[u][i];
            if(!vis[v]){
                vis[v]=1;
                d[v]=d[u]+1;
                if(d[v]>ans){
                    ans=d[v];
                    t=v;
                }
                q.push(v);
            }
        }
    }
    return ans;
}
int main()
{
    int i,j,n,m,x,y;
    while(~scanf("%d%d",&n,&m)&&(n+m)){
        memset(head,-1,sizeof(head));
        cnt=0;
        for(i=0;i<m;++i){
            scanf("%d%d",&x,&y);
            add(--x,--y);
            add(y,x);
        }
        find_bcc(n);
        for(i=1;i<=bcc_cnt;++i) G[i].clear();
        for(i=0;i<n;++i)
        for(j=head[i];~j;j=edge[j].next){
            int v=edge[j].v;
            if(bccno[v]!=bccno[i]) G[bccno[i]].push_back(bccno[v]);
        }
        int p,ans=bfs(1,p);
        ans=max(ans,bfs(p,p));
        printf("%d\n",bcc_cnt-1-ans);
    }
    return 0;
}


#pragma comment(linker,"/STACK:1024000000,1024000000")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值