Codeforces 231E - Cactus

231E - Cactus

给一个10^5个点的无向图,每个点最多属于一个环,规定两点之间的简单路:从起点到终点,经过的边不重复

给10^5个询问,每个询问两个点,问这两个点之间有多少条简单路。

 

挺综合的一道题目,无向图连通分量,缩点,LCA 都考察到了。。

 

因为每个点最多属于一个环,因此把所有环缩点,就可以得到一棵树

然后对于每个询问,用LCA查找从起点到终点有多少个环

 

并查集处理的时候挂了一发,注意LCA时合并两个并查集,根节点深度小的作为父亲。。

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#include<string>
#include<sstream>
#define eps 1e-9
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define MAXN 100005
#define MAXM 400005
#define INF 0x3fffffff
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define clr(x,y) memset(x,y,sizeof(x));
using namespace std;
typedef long long LL;
int i,j,k,n,m,x,y,T,big,cas,len;
bool flag;

int edge,head[MAXN],bin[MAXN],headS[MAXN],edgeS,vis[MAXN],d[MAXN],id[MAXN],num[MAXN];
struct edgenode
{
    int to,next,flag;
} G[MAXM],S[MAXM];

void add_edge(int x,int y)
{
    G[edge].to=y;
    G[edge].flag=0;
    G[edge].next=head[x];
    head[x]=edge++;
}

void add_edgeS(int x,int y)
{
    S[edgeS].to=y;
    S[edgeS].flag=0;
    S[edgeS].next=headS[x];
    headS[x]=edgeS++;
}

int fa[MAXN];
int findset(int x)
{
    return x==fa[x]?x:fa[x]=findset(fa[x]);
}
void unionset(int x,int y)
{
    fa[findset(x)]=findset(y);
}

int dfn[MAXN],low[MAXN],time;
void dfs(int u,int fa)
{
    vis[u]=1;
    dfn[u]=low[u]=++time;
    for (int i=head[u];i!=-1;i=G[i].next)
    {
        int v=G[i].to;
        if (v!=fa && vis[v]==1)
        {
            low[u]=min(low[u],dfn[v]);
        }

        if (!vis[v])
        {
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            if (low[v]>dfn[u]) G[i].flag=1;
        }
    }
    vis[u]=2;
}

void dive(int u,int scc)
{
    id[u]=scc;
    vis[u]=1;
    num[scc]++;
    for (int i=head[u];i!=-1;i=G[i].next)
    {
        if (!G[i].flag && !vis[G[i].to])
            dive(G[i].to,scc);
    }
}

void dis(int u,int dep)//记录当前节点到根节点有多少个“环”
{
    vis[u]=1;
    d[u]=dep;
    for (int i=headS[u];i!=-1;i=S[i].next)
    {
        int v=S[i].to;
        if (!vis[v])
        {
            if (num[v]>2) dis(v,dep+1);
            else dis(v,dep);
        }
    }
}

vector<pair<int,int> > Q[MAXN];
int ans[MAXN];
void tarjan(int u)
{
    vis[u]=true;
    for (int i=0;i<Q[u].size();i++)
    {
        int v=Q[u][i].X,id=Q[u][i].Y;
        if (vis[v])
        {
            int com=findset(v);
            ans[id]=d[u]+d[v]-2*d[com];
            if (num[com]>2) ans[id]++;
        }
    }

    for (int i=headS[u];i!=-1;i=S[i].next)
    {
        int v=S[i].to;
        if (!vis[v])
        {
            tarjan(v);
            unionset(v,u);
        }
    }
}


int main()
{
    memset(head,-1,sizeof(head));
    edge=0;
    memset(headS,-1,sizeof(headS));
    edgeS=0;
    scanf("%d%d",&n,&m);
    while (m--)
    {
        scanf("%d%d",&x,&y);
        add_edge(x,y);
        add_edge(y,x);
    }

    dfs(1,-1); 

    memset(vis,0,sizeof(vis));
    int scc=1;
    for (int i=1;i<=n;i++)
    {
        if (!vis[i]) dive(i,scc++);
    }

    for (i=1;i<=n;i++)
    {
        for (int j=head[i];j!=-1;j=G[j].next)
        {
            int v=G[j].to;
            if (id[i]!=id[v])
            {
                add_edgeS(id[i],id[v]);
                add_edgeS(id[v],id[i]);
            }
        }
    }

    int q;
    scanf("%d",&q);
    for (i=0;i<q;i++)
    {
        scanf("%d%d",&x,&y);
        x=id[x];y=id[y];
        Q[x].PB(MP(y,i));
        Q[y].PB(MP(x,i));
    }

    memset(vis,0,sizeof(vis));
    if (num[1]>2) dis(1,1); else dis(1,0);

    memset(vis,0,sizeof(vis));
    for (i=1;i<=n;i++) fa[i]=i;
    tarjan(1);

    bin[0]=1;
    for(int i=1;i<MAXN;i++)
        bin[i]=bin[i-1]*2%1000000007;
    for (int i=0;i<q;i++)
    {
        printf("%d\n",bin[ans[i]]);
    }
    return 0;
}

 

posted @ 2015-08-02 16:47  zhyfzy  阅读(476)  评论(0编辑  收藏  举报