poj3694 Network 边双联通缩点+离线LCA

11 篇文章 0 订阅

      给一个联通的图,然后依次添加Q条边,每次添加边之后,图中还剩多少条桥?先求一下边双联通重构成一棵树,然后就是求LCA了,查询的时候从两个点开始往祖先一边爬一边标记掉沿途的边,根据每次标记掉的点以及上一次的答案就可以递推出当前的答案...切记一点这题有重边...所以求ebc的时候记得用边判父节点...思路不是很难,但代码写起来太坑爹了......

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
typedef long long ll;
const int maxn=155000;
struct EDGE
{
    int v,w,next;
}ed1[maxn<<2],ed2[maxn<<2],ed3[4040];
int g[maxn];
int gg[maxn];
int qe[maxn];

int p1,p2,p3;

stack<int> s;
int pre[maxn];
int id[maxn],dfs_clock,ebc_cnt;
int deg[maxn];

bool ok[maxn];

int n,m,k,p,q;
int dfs(int u,int frm)
{
    int lowu=pre[u]=++dfs_clock;
    s.push(u);
    for (int j=g[u];j!=-1; j=ed1[j].next)
    {
        int v=ed1[j].v;
        if (j==(frm^1)) continue;
        if (!pre[v])
        {
            int lowv=dfs(v,j);
            lowu=min(lowu,lowv);
        }
        else if (pre[v]<pre[u])
        {
            lowu=min(lowu,pre[v]);
        }
    }
    if (lowu==pre[u])
    {
        ebc_cnt++;
        while (true)
        {
            int x=s.top();
            s.pop();
            id[x]=ebc_cnt;
            if (x==u) break;
        }
    }
    return lowu;
}
void findebc(int n)
{
    memset(pre,0,sizeof pre);
    memset(id,0,sizeof id);
    while(!s.empty()) s.pop();
    for (int i=1; i<=n; i++)
    {
        if (!pre[i]) dfs(i,-1);
    }
}

int dis[maxn];
bool vis[maxn];
int fa[maxn];
int res[maxn][3];
int fr[maxn];

int find(int x)
{
    if (x==fa[x]) return fa[x];
    return fa[x]=find(fa[x]);
}
void lca(int u)
{
    vis[u]=true;
    fa[u]=u;
    int v;
    for (int j=qe[u]; j!=-1; j=ed3[j].next)
    {
        v=ed3[j].v;
        if (vis[v])
        {
            res[ed3[j].w][2]=find(v);
        }
    }
    for (int j=gg[u]; j!=-1; j=ed2[j].next)
    {
        v=ed2[j].v;

        if (!vis[v])
        {
            fr[v]=u;
            dis[v]=dis[u]+1;
            lca(v);
            fa[v]=u;
        }
    }
}
int ans[maxn];
int main()
{
//    freopen("in.txt","r",stdin);
    int x,y;
    int cp=0;
    while(~scanf("%d%d",&n,&m))
    {
        if (n==0 && m==0) break;
        cp++;
        printf("Case %d:\n",cp);
        memset(g,-1,sizeof g);
        memset(gg,-1,sizeof gg);
        memset(qe,-1,sizeof qe);
        p1=p2=p3=0;

        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);
            ed1[p1].v=y;
            ed1[p1].w=1;
            ed1[p1].next=g[x];
            g[x]=p1;
            p1++;

            ed1[p1].v=x;
            ed1[p1].w=1;
            ed1[p1].next=g[y];
            g[y]=p1;
            p1++;
        }
        ebc_cnt=0;
        findebc(n);
        for (int i=1; i<=n; i++)
            for (int j=g[i]; j!=-1; j=ed1[j].next)
            {
                x=id[i];
                y=id[ed1[j].v];
                if (x!=y)
                {
                    ed2[p2].v=y;
                    ed2[p2].w=1;
                    ed2[p2].next=gg[x];
                    gg[x]=p2;
                    p2++;

                    ed2[p2].v=x;
                    ed2[p2].w=1;
                    ed2[p2].next=gg[y];
                    gg[y]=p2;
                    p2++;
                }
            }
        int sum=ebc_cnt-1;
        memset(vis,false,sizeof vis);
        memset(res,0,sizeof res);
        scanf("%d",&q);
        for (int i=1; i<=q; i++)
        {
            scanf("%d%d",&x,&y);
            x=id[x];
            y=id[y];

            ed3[p3].v=y;
            ed3[p3].w=i;
            ed3[p3].next=qe[x];
            qe[x]=p3;
            p3++;
            ed3[p3].v=x;
            ed3[p3].w=i;
            ed3[p3].next=qe[y];
            qe[y]=p3;
            p3++;

            res[i][0]=x;
            res[i][1]=y;
        }
        dis[0]=0;
        lca(1);
        memset(ans,0,sizeof ans);
        memset(ok,false,sizeof ok);
        ans[0]=sum;
        for (int i=1; i<=q; i++)
        {
            x=res[i][0];
            y=res[i][1];
            int t=res[i][2];
            ans[i]=ans[i-1];
            while (x!=t)
            {
                if (!ok[x]) ans[i]--,ok[x]=true;
                x=fr[x];
            }
            while (y!=t)
            {
                if (!ok[y]) ans[i]--,ok[y]=true;
                y=fr[y];
            }

        }
        for (int i=1; i<=q; i++)
        printf("%d\n",ans[i]);
        printf("\n");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值