【7.89%】【BNUOJ 52303】Floyd-Warshall

Time limit: 2 seconds
Memory limit: 1024 megabytes

In ICPCCamp, there are n cities and m (bidirectional) roads between cities. The i-th road is between the
ai-th city and the bi-th city. There may be roads connecting a citie to itself and multiple roads between
the same pair of cities.
Bobo has q travel plans. The i-th plan is to travel from the ui-th city to the vi-th city. He would like
to know the smallest number of roads needed to travel for each plan. It is guaranteed that cities are
connected.
【Input】
The first line contains 3 integers n, m, q (1 ≤ n ≤ 10^5, 0 < m − n < 100, 1 ≤ q ≤ 10^5).
The i-th of the following m lines contains 2 integers ai, bi (1 ≤ ai, bi ≤ n).
The i-th of the last q lines contains 2 integers ui, vi (1 ≤ ui, vi ≤ n)

【Output】
n lines with integers l1, l2, … , ln. li denotes the smallest number of roads travelling from city ui to city vi.
Examples

【standard input 1】
4 5 3
1 2
1 3
1 4
2 3
3 4
2 2
2 3
2 4
【standard output 1】
0
1
2

【standard input 1】
1 2 1
1 1
1 1
1 1
【standard output 1】
0

【题解】

毒题。
讲一下做法:
注意到m最多为n+100最小为n
我们先在m条边中取出n-1条。让它组成一棵树。(用并查集去除多余的边,但是这些多余的边不能删掉要记录下来);
对于每一个询问x,y;先求出它们的最近公共祖先z;然后设某个节点x的深度为dep[x]
则两个点之间的最短路一定是dep[x]+dep[y]-2*dep[z];
但是这只是在去掉了多余的边后的情况。
我们还要把那些边再加进来。
全部都加进来之后。
从加进来的多余的边的端点开始进行spfa。
设询问仍然是x,y;
则再尝试用dis[s][x]+dis[s][y]来更新答案。其中s∈{x|x为多余边的端点}
这里的dis第一维只要开到200多就可以了。
相当于我们在一棵树上进行了一次最短路。
然后再用多余的边上的点作为起点进行最短路。
如果那些多余边上的点是最短路上的点。那么在第二次更新dis[s][x]+dis[s][y]的时候都会涉及到。
可以理解为x->y经过了s这个点的最短路。枚举s。取最小。
添加了边。最短路发生改变。那么新的最短路肯定经过某一条新添加的边的两端点。
很棒的思路。
最后是LCA的实现思路:
盗了几张图:
这里写图片描述
这里写图片描述

我发两份代码:
第一份用的是RMQ预处理出LCA的方法。可以AC;
第二份用的是树上倍增的方法,但是TLE。(供学习);

//代码一,可AC
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 4e5 + 10;
const int MAX_REST = 100 + 10;
const int INF = 2100000000;

int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[25];
queue <int> dl;
int dis[MAX_REST * 2][MAXN];

struct bian
{
    int x, y;
};

vector <int> a[MAXN], c;
vector < pair<int, int> > rest, b;
int f[MAXN], re = 0;
int p[MAXN][25];
int fi[MAXN], cnt = 0;
int shall[MAXN * 2];

void input(int &num)
{
    num = 0;
    char c;
    do
    {
        c = getchar();
    } while (!isdigit(c));
    while (isdigit(c))
    {
        num = num * 10 + c - '0';
        c = getchar();
    }
}

int ff(int x)
{
    if (f[x] == x)
        return f[x];
    else
        f[x] = ff(f[x]);
    return f[x];
}

int lca(int x, int y)
{
    int xx = fi[x];
    int yy = fi[y];
    if (xx > yy)
        swap(xx, yy);
    int k = shall[yy - xx + 1];
    int a1 = p[xx][k];
    int a2 = p[yy - pr[k] + 1][k];//左边和右边都搞下才能覆盖整个区间
    if (dep[a1] < dep[a2])
        return a1;
    else
        return a2;
}

void dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    p[++cnt][0] = u;//dfs序列为cnt,长度为2^0的深度最小的节点
    fi[u] = cnt;
    int len = a[u].size();
    for (int i = 0; i <= len - 1; i++)
    {
        int y = a[u][i];
        if (y != fa)
        {
            dfs(y, u);
            p[++cnt][0] = u;
        }
    }
}

void spfa(int s)
{
    for (int i = 1; i <= n; i++)//用memset会比较慢
        dis[s][i] = INF;
    dis[s][c[s]] = 0;
    dl.push(c[s]);
    while (!dl.empty())
    {
        int x = dl.front();
        dl.pop();
        int len = a[x].size();
        for (int i = 0; i <= len - 1; i++)
        {
            int y = a[x][i];
            if (dis[s][y]  >dis[s][x] + 1)
            {
                dis[s][y] = dis[s][x] + 1;
                dl.push(y);
            }
        }
    }
}

int main()
{
    //freopen("F:\\rush.txt", "r", stdin);
    input(n); input(m); input(q);
    for (int i = 1; i <= n; i++)
        f[i] = i;
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        input(x); input(y);
        if (x == y)
            continue;
        int r1 = ff(x), r2 = ff(y);
        if (r1 != r2)
        {
            f[r1] = r2;
            a[x].push_back(y);
            a[y].push_back(x);
        }
        else
            rest.push_back(make_pair(x, y));
    }
    dfs(1, 0);
    pr[0] = 1;
    for (int i = 1; i <= 24; i++)
        pr[i] = pr[i - 1] << 1;
    shall[1] = 0;
    int now = 1;
    for (int i = 2; i <= cnt; i++)//shall[i]表示长度为i的区间需要2的多少次方来覆盖。
    {
        if (i == pr[now])
        {
            shall[i] = shall[i - 1] + 1;
            now++;
        }
        else
            shall[i] = shall[i - 1];
    }
    for (int i = 1; pr[i] <= cnt; i++)
        for (int j = 1; j + pr[i] - 1 <= cnt; j++)//用RMQ来做
        {
            int x = p[j][i - 1];
            int y = p[j + pr[i - 1]][i - 1];
            if (dep[x] < dep[y])
                p[j][i] = x;
            else
                p[j][i] = y;
        }
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        input(x); input(y);
        b.push_back(make_pair(x, y));
        ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
    }
    int len = rest.size();//把多余的边加入图中
    for (int i = 0; i <= len - 1; i++)
    {
        int x = rest[i].first, y = rest[i].second;
        a[x].push_back(y);
        a[y].push_back(x);
        c.push_back(x);
        c.push_back(y);//并取出边的两端点。用来作为spfa的起点
    }
    len = c.size();
    for (int i = 0; i <= len - 1; i++)
        spfa(i);
    for (int i = 0; i <= q - 1; i++)
    {
        int x, y;
        x = b[i].first, y = b[i].second;
        for (int j = 0; j <= len - 1; j++)//需要更新的肯定是那些新添加的点。
        {
            int s = j;
            ans[i + 1] = min(ans[i + 1], dis[s][x] + dis[s][y]);
        }
        printf("%d\n", ans[i + 1]);
    }
    return 0;
}
//代码二 会超时 树上倍增
//递推公式:p[i][j] = p[p[i][j-1][j-1];
//p[i][j]表示从i节点往上走2^j个节点后是什么节点。
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 4e5;
const int MAX_REST = 100 + 10;
int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[20];
queue <int> dl;
int dis[MAX_REST*2][MAXN];
struct bian
{
    int x, y;
};
vector <int> a[MAXN],c;
vector < pair<int, int> > rest,b;
int f[MAXN],re = 0,p[MAXN][20];
int ff(int x)
{
    if (f[x] == x)
        return f[x];
    else
        f[x] = ff(f[x]);
    return f[x];
}
int lca(int x, int y)//倍增求LCA
{
    if (dep[x] > dep[y])
        swap(x, y);
    for (int i = 17; i >= 0; i--)
        if (dep[x] <= dep[y] - (pr[i]))
            y = p[y][i];
    if (x == y)//如果x直接等于y了要直接返回;
        return x;
    for (int i = 17; i >= 0; i--)
    {
        if (p[x][i] == p[y][i]) continue;
        x = p[x][i], y = p[y][i];
    }
    return p[x][0];
}
void dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    p[u][0] = fa;
    for (int i = 1; i <= 17; i++)
        p[u][i] = p[p[u][i - 1]][i - 1];
    int len = a[u].size();
    for (int i = 0;i <= len-1;i++)
    {
        int y = a[u][i];
        if (y != fa)
            dfs(y, u);
    }
}
void spfa(int s)
{
    memset(dis[s], 127 / 3, sizeof(dis[s]));
    dis[s][c[s]] = 0;
    dl.push(c[s]);
    while (!dl.empty())
    {
        int x = dl.front();
        dl.pop();
        int len = a[x].size();
        for (int i = 0; i <= len - 1; i++)
        {
            int y = a[x][i];
            if (dis[s][y] > dis[s][x] + 1)
            {
                dis[s][y] = dis[s][x] + 1;
                dl.push(y);
            }
        }
    }
}
int main()
{
    //freopen("F:\\rush.txt", "r", stdin);
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        f[i] = i;
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        if (x == y)
            continue;
        int r1 = ff(x), r2 = ff(y);
        if (r1 != r2)
        {
            f[r1] = r2;
            a[x].push_back(y);
            a[y].push_back(x);
        }
        else
            rest.push_back(make_pair(x, y));
    }
    dfs(1, 0);
    pr[0] = 1;
    for (int i = 1; i <= 17; i++)
        pr[i] = pr[i - 1] << 1;
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        b.push_back(make_pair(x, y));
        ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
    }
    int len = rest.size();
    for (int i = 0; i <= len - 1; i++)
    {
        int x = rest[i].first, y = rest[i].second;
        a[x].push_back(y);
        a[y].push_back(x);
        c.push_back(x);
        c.push_back(y);
    }
    len = c.size();
    for (int i = 0; i <= len - 1; i++)
        spfa(i);
    for (int i = 0; i <= q-1; i++)
    {
        int x, y;
        x = b[i].first, y = b[i].second;
        for (int j = 0; j <= len - 1; j++)
        {
            int s = j;
            ans[i+1] = min(ans[i+1], dis[s][x] + dis[s][y]);
        }
        printf("%d\n", ans[i+1]);
    }
    return 0;
}
posted @ 2017-10-06 19:23  AWCXV  阅读(130)  评论(0编辑  收藏  举报