poj3694 连通图+LCA

来源:互联网 发布:单片机电路设计 编辑:程序博客网 时间:2024/05/01 02:12

不缩点,直接在原图中找LCA,求桥时记录每个节点的父亲节点

找LCA时,先将两点上升到同一层次,然后一起再向上找父亲节点,遇到桥就把桥标记删除,画一下图就清楚了
#include <iostream>#include<stdio.h>#include<cstring>#include<algorithm>#include<queue>#include<math.h>using namespace std;#define maxn 100010#define FOR(i,j,k) for(int i=j;i<=k;i++)struct edge{    int to,next;}edge[400010];int chu[maxn],ru[maxn];//强连通分量的出度,入度int dfn[maxn];//时间戳,该点最早发现时间int low[maxn];//该点及其后代能连回的最早祖先值int vis[maxn],stack[maxn],head[maxn];int belong[maxn];//每个点属于几号分量int fa[maxn];bool bridge[maxn];int n;int tot;//一共多少条边int cnt;//一共多少分量int ans;//一共多少桥int times,top;void init(){    tot=0,cnt=0,times=0,top=0,ans=0;    memset(head,-1,sizeof(head));    memset(dfn,0,sizeof(dfn));    memset(low,0,sizeof(low));   // memset(belong,0,sizeof(belong));   // memset(chu,0,sizeof(chu));   // memset(ru,0,sizeof(ru));    memset(vis,0,sizeof(vis));    memset(stack,0,sizeof(stack));    memset(bridge,0,sizeof(bridge));    FOR(i , 1, n) fa[i] = i;}void addedge(int from,int to){    tot++;    edge[tot].to=to;    edge[tot].next=head[from];    head[from]=tot;}void tarjan(int u){    int v,i;    times++;    top++;    dfn[u]=low[u]=times;    vis[u]=1;    stack[top]=u;    for (i=head[u];i!=-1;i=edge[i].next)    {        v=edge[i].to;        if( v== fa[u]) continue;        if (vis[v]==0) //树边        {            fa[v] =u;            tarjan(v);            low[u]=min(low[u],low[v]);            if(low[v] >dfn[u] )            {                ans ++;                bridge[v] =true;            }        }        if (vis[v]==1)            low[u]=min(low[u],dfn[v]);    }    /*if (dfn[u] == low[u])    {        cnt++;        while(1)        {            v=stack[top];            belong[v]=cnt;            vis[v]=2;            top--;            if(u == v) break;        }    }*/    vis[u]=2;}void lca(int u,int v){    if(dfn[u] > dfn[v])        swap( u, v);    while( dfn[u] < dfn[v])    {        if(bridge [v])            ans--,bridge[v]=0;        v=fa[v];    }    while( u != v)    {        if(bridge[u])            ans--,bridge[u]=0;        if(bridge[v])            ans--,bridge[v]=0;        u=fa[u],v=fa[v];    }}int main(){    int n,m,q;    int cas=0;    while(~scanf("%d%d",&n,&m)){        if (n==0 && m==0)            break;        cas ++ ;        init();        while(m--)        {            int u,v;            scanf("%d%d",&u,&v);            addedge(u,v);            addedge(v,u);        }        for(int i=1;i<=n;i++)            if(!vis[i])                tarjan(i);        scanf("%d" ,&q);        printf("Case %d:\n", cas);        while(q--)        {            int u,v;            scanf("%d%d" ,&u,&v);            lca(u,v);            printf("%d\n",ans);        }        printf("\n");    }    return 0;}

间遇到桥就把桥的标记删除,画一下图就清楚了
0 0
原创粉丝点击