JOJ2737:狼与羊的故事(求图上任意两点间的桥边)

 2737: 狼与羊的故事


ResultTIME LimitMEMORY LimitRun TimesAC TimesJUDGE
3s65536K5310Standard

村长要召开羊族大会,讨论羊族未来的发展,要求羊羊们到指定地点集合。小羊们收到通知后从家里出发到达指定地点。每个羊的家都是与其他羊的家连通的,可以互相访问。 比如说可以从1到3,同样也可以从3到1 。灰太郎听到这个消息后,准备有所行动(你懂的)。但他不知道应该在哪条路上等羊。 但他知道,有的路线一定要进过某条边。如上图从1到5一定要经过4-5这条路,这样他可以在那条路上事先埋伏好。现在他想知道2个点之间有多少这样的路。如果没有输出0.

Input

第一行有两个整数N,M。表示有N(1<N<10001)个家,M(1<M<10001)条路线。随后有M行,每行有两个不相同的整数A、B表示 A,B之间有一条路。我们保证任意时刻任意两个地点都能够相互到达并且2点之间最多只有1条路。接下来输入一个数K(K<20001)表示有K次查询。随后有K行。 每行有两个不相同的整数A、B询问 A,B之间有多少条的那样的路。

Output

对于每个询问输出有多少条那样的路。

Sample Input

5 5
1 2
3 4
2 4
3 1
5 4
4
1 5
3 5
2 3
4 1

Sample Output

1
1
0
0

Problem Source: 毛有祥



大概思路:求桥边,之后去掉缩点,之后求LCA。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <time.h>
using namespace std;

const int MAXN = 10001;
const int MAXK = 20010;
const int MAXQ = 20010;

struct Edge
{
    int end;
    int next;
} st[MAXK * 2], ans[MAXK * 2], ss[MAXK * 2];

int head[MAXN], father[MAXN], dfn[MAXN], low[MAXN], root[MAXN];
int parent[MAXN], headq[MAXN], dis[MAXN], p[MAXN], Head[MAXN];
bool flag[MAXN];
int cnt, n, m, iindex, top, scnt, nnum;

struct Que
{
    int next, b, num;
} query[MAXQ * 2];

int qq[MAXQ];
inline int min(int a, int b)
{
    return a > b ? b : a;
}

void init()
{
	memset(head, -1, sizeof(int) * (n + 1));
	memset(father, -1, sizeof(int) * (n + 1));
	memset(dfn, -1, sizeof(int) * (n + 1));
	memset(low, -1, sizeof(int) * (n + 1));
	memset(root, -1, sizeof(int) * (n + 1));
	memset(parent, -1, sizeof(int) * (n + 1));
	memset(headq, -1, sizeof(int) * (n + 1));
	memset(dis, -1, sizeof(int) * (n + 1));
	memset(flag, false, sizeof(bool) * (n + 1));
	memset(p, -1, sizeof(int) * (n + 1));
	memset(Head, -1, sizeof(int) * (n + 1));

	for (int i = 0; i < n; ++i)
	{
	    parent[i] = i;
	    root[i] = 1;
	}
	nnum = scnt = cnt = iindex = top = 0;
}

int find(int a)
{
    int e = a, j;
    while (root[e] == 0) e = parent[e];
    while (a != e)
    {
        j = parent[a];
        parent[a] = e;
        a = j;
    }
    return e;
}

void uf(int a, int b)
{
    a = find(a);
    b = find(b);
    if (a == b) return;
    if (root[a] >= root[b])
    {
        parent[b] = a;
        root[a] += root[b];
        root[b] = 0;
    }
    else
    {
        parent[a] = b;
        root[b] += root[a];
        root[a] = 0;
    }
}

void add(int a, int b)
{
    st[cnt].next = head[a];
    st[cnt].end = b;
    head[a] = cnt;
    cnt++;
}

void dfs(int u)
{
    Edge e;
    int v;
    low[u] = dfn[u] = iindex++;
    for (int i = head[u]; i != -1; i = st[i].next)
    {
        v = st[i].end;
        if (dfn[v] == -1)
        {
            father[v] = u;
            dfs(v);
            low[u] = min(low[u], low[v]);
            if (low[v] == dfn[v])
            {
                e.end = u;
                e.next = v;
                ans[top++] = e;
            }
            else
            {
                uf(u, v);
            }
        }
        else
        if (v != father[u])
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    return;
}

void add2(int a, int b)
{
    ss[nnum].next = Head[a];
    ss[nnum].end = b;
    Head[a] = nnum;
    nnum++;
}

void add3(int a, int b, int c)
{
    query[scnt].next = headq[a];
    query[scnt].b = b;
    query[scnt].num = c;
    headq[a] = scnt;
    scnt++;
}

int find1(int u)
{
    if (p[u] == -1) return u;
    else
    {
        p[u] = find1(p[u]);
        return p[u];
    }
}

void tarjan(int u, int dep)
{
    int a, b;
    dis[u] = dep;
    flag[u] = true;
    for (int i = Head[u]; i != -1; i = ss[i].next)
    {
        a = ss[i].end;
        if (!flag[a])
        {
            tarjan(a, dep + 1);
            p[a] = u;
        }
    }
    for (int i = headq[u]; i != -1; i = query[i].next)
    {
        a = query[i].b;
        b = query[i].num;
        if (flag[a])
        {
            qq[b] = dis[a] + dis[u] - 2 * dis[find1(a)];
        }
    }
}

int main()
{
    int a, b, q;
    while (scanf("%d%d", &n, &m) != EOF)
    {
        init();
        for (int i = 0; i < m; ++i)
        {
            scanf("%d%d", &a, &b);
            add(a, b);
            add(b, a);
        }
        dfs(0);
        scanf("%d", &q);
        for (int i = 0; i < q; ++i)
        {
            scanf("%d%d", &a, &b);
            a = find(a);
            b = find(b);
            if (a == b) qq[i] = 0;
            else
            {
                add3(a, b, i);
                add3(b, a, i);
            }
        }
        for (int i = 0; i < top; ++i)
        {
            a = ans[i].end;
            b = ans[i].next;
            a = find(a);
            b = find(b);
            add2(a, b);
            add2(b, a);
        }
        tarjan(a, 0);
        for (int i = 0; i < q; ++i)
        {
            printf("%d\n", qq[i]);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值