zoukankan      html  css  js  c++  java
  • 【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
    ", 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
    ", ans[i+1]);
        }
        return 0;
    }
  • 相关阅读:
    Web Ajax入门一讲
    Delphi – 我的代码之简单五子棋
    闲话 纪念我的4520G
    Delphi 我的代码之窗体移动
    破文 黑客游戏
    破文 OD常用断点
    Web 简单的开始 – Ajax + XML +DOM
    工具 – XMLSPY 和 UModel 商业版 2010v12.0有注册机
    API InterlockedCompareExchange用法
    软件工程 设计模式学习之策略模式Strategy
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632194.html
Copyright © 2011-2022 走看看