zoukankan      html  css  js  c++  java
  • 时空宝石(Floyd + 线段树)

    题目描述

    zP1nG很清楚自己打不过灭霸,所以只能在自己出的题里欺负他。

    咳咳。这一次他得到了空间宝石(Tesseract)

    世界之树连接着九界,此时灭霸和zP1nG都在九界的某个地方。而九界是相互无法到达的。zP1nG为了追杀灭霸,决定使用空间宝石的力量到达灭霸身边。因为zP1nG不擅长使用空间宝石,无法直接开一条从自己的位置到灭霸的位置的传送门,所以他只能无意识地使用空间宝石的力量。zP1nG想知道,在自己胡乱地使用空间宝石后,通过传送门到达灭霸的位置最少需要多长时间。

    具体地,九界的编号为0~8,共有(n)道传送门,第i道传送门为优先级为(p_i),由(u_i)(v_i),需要花费(w_i)个时间的单向门。传送的规则为:zP1nG按顺序穿过的传送门的优先级必须是单调不降的。例如,zP1nG穿过了三道传送门到达灭霸的位置,这三道传送门的优先级分别为1→2→2即为一种合法的方式,而优先级分别为1→2→1是不合法的。

    zP1nG会使用(q)次宝石的力量来尝试进行传送:其中第(i)次尝试会打开数道传送门,这些传送门的优先级会形成(s_i)个区间。例如,在某次尝试中zP1nG打开了三个区间([1,2])([4,7])([9,10]),那么优先级在这三个区间内的传送门将全部被打开并允许zP1nG穿过。你需要告诉zP1nG在此情况下从自己的位置(z_i)到达灭霸所在处(t_i)所需的最短时间。尝试结束后所有传送门会关闭。

    输入格式

    (1)行包含两个正整数(n)(S)(S)的含义见数据范围与约定所述。

    (2)(n+1)行每行包含4个正整数(p_i)(u_i)(v_i)(w_i)

    (n+2)行包含一个正整数(q)

    (n+3)至第(n+q+2)行每行若干个正整数,其中前三个数为(z_i)(t_i)(s_i),之后为(2 imes si)个正整数,表示每个区间的左右端点。

    各变量具体含义见题目描述所述。

    输出格式

    对于zP1nG进行的每次尝试,输出一行一个数表示从zP1nG的位置到灭霸的位置所需的最短时间,如果zP1nG无法到达灭霸的位置则输出-1

    样例

    样例输入

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

    样例输出

      -1
      8
      5
      2
    

    数据范围与约定

    5为无特殊限制

    题解

    神仙题,考试的时候一头雾水,看着像最短路但总是缺点什么。要知道道路的优先级单凭一次的Dij或Spfa都是无法解决的,因为无论怎么加限制,我们都无法保证在松弛时所做的那一个选择一定是最优的,这点大家可以通过手动模拟数据试验一下。

    所以这道题不能用大众的Dij或Spfa来解决。

    面对这种限制条件极其恶心,可能情况极其多的类型,最好的办法,那就是把所有情况都算一遍!

    那么我们剩下的,也就是只有万年小冷门(其实并没有)的Floyd了!

    其实题干中已经说明节点数一共也就有9个,也就是说我们完全可以用联接矩阵来存储整张图,(n^3)的效率完全可以接受,这就给了Floyd操作的空间。而对于优先级的问题,通过研究数据我们知道优先级最多有2000个,在(9^3)的基础上再乘上2000,这样的时空效率我们也是可以接受的。所以我们的办法就是针对每一个优先级都建一个联接矩阵,然后以等级作为区间构建线段树。

    没错,你没有看错,就是线段树。鬼知道最短路还能放在线段树上,活久见。

    唯一能够有所提示的地方估计就是每次给出的优先级都是区间吧...对优先级建树,每个树节点都有一个矩阵,(dis[i][j]) 代表在当前树节点包含的区间(即优先级区间)内 (i)(j) 的最短路。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int maxn = 2000 + 10;
    char buf[1 << 20], *p1 = buf, *p2 = buf;
    // fread版快读,输入结束后手动输入EOF结束输入
    char getc() {
        if (p1 == p2) {
            p1 = buf;
            p2 = buf + fread(buf, 1, 1 << 20, stdin);
            if (p1 == p2)
                return EOF;
        }
        return *p1++;
    }
    inline int read() {
        int s = 0, w = 1;
        char c = getc();
        while (c < '0' || c > '9') {
            if (c == '-')
                w = -1;
            c = getc();
        }
        while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
        return s * w;
    }
    struct node {
        int l, r;
        bool operator<(const node& x) const { return l < x.l; }
    } q[maxn];  //存储每一个可以打开的区间
    struct Maxtrix {
        int dis[10][10];
        void Init() {
            //有点类似于单位矩阵的初始化
            for (int i = 0; i < 9; i++)
                for (int j = 0; j < 9; j++) dis[i][j] = (i == j ? 0 : 0x3f3f3f3f);
        }
    } G[maxn];  //为每一个优先级都建一张图
    struct tree {
        Maxtrix data;
        int l, r;
    } t[maxn << 2];
    inline Maxtrix Update(Maxtrix a, Maxtrix b) {
        Maxtrix c;
        c.Init();
        //标准的floyd最短路 ,又有点像矩阵乘法
        for (int k = 0; k < 9; k++)
            for (int i = 0; i < 9; i++)
                for (int j = 0; j < 9; j++) c.dis[i][j] = min(c.dis[i][j], a.dis[i][k] + b.dis[k][j]);
        return c;
    }
    #define tl t[u].l
    #define tr t[u].r
    #define ls (u << 1)
    #define rs (u << 1 | 1)
    void Pushup(int u) { t[u].data = Update(t[ls].data, t[rs].data); }
    void Build(int u, int l, int r) {
        //每个线段树节点的所代表的区间都是优先级的区间
        t[u].l = l;
        t[u].r = r;
        if (l == r) {
            t[u].data = G[l];
            return;
        }
        int mid = (tl + tr) >> 1;
        Build(ls, l, mid);
        Build(rs, mid + 1, r);
        Pushup(u);
    }
    Maxtrix Ask(int u, int l, int r) {
        if (l <= tl && tr <= r) {
            return t[u].data;
        }
        int mid = (tl + tr) >> 1;
        bool flag = 0;
        Maxtrix ans;
        if (l <= mid) {
            ans = Ask(ls, l, r);
            flag = 1;
        }
        if (r > mid) {
            if (flag)
                ans = Update(ans, Ask(rs, l, r));
            else
                ans = Ask(rs, l, r);
        }
        return ans;
    }
    int main() {
        int n = read(), S = read();
        for (int i = 1; i <= 2001; i++) G[i].Init();
        int p, u, v, w;
        for (int i = 1; i <= n; i++) {
            p = read(), u = read(), v = read(), w = read();
            G[p].dis[u][v] = min(G[p].dis[u][v], w);
        }
        for (register int t = 1; t <= 2001; t++)  //事先预处理单独每一级优先级的最短路
            for (register int k = 0; k < 9; k++)
                for (register int i = 0; i < 9; i++)
                    for (register int j = 0; j < 9; j++)
                        G[t].dis[i][j] = min(G[t].dis[i][j], G[t].dis[i][k] + G[t].dis[k][j]);
        //线段树主要是解决涉及多个优先级的最短路
        //按照优先级作为区间构造线段树,每一个区间内的树节点所记录的数据,就是每一个优先级区间内所能达到的最短路
        Build(1, 1, 2001);
        int cas = read();
        int st, ed, siz;
        while (cas--) {
            st = read(), ed = read(), siz = read();
            for (register int i = 1; i <= siz; i++)  //读入每个可使用的优先级区间
                q[i].l = read(), q[i].r = read();
            sort(q + 1, q + 1 + siz);
            Maxtrix ans = Ask(1, q[1].l, q[1].r);  //我们按照优先级作为区间构造了线段树,更新答案时直接在相应的区间内查询即可
            for (register int i = 2; i <= siz; i++) {
                ans = Update(ans, Ask(1, q[i].l, q[i].r));
            }
            printf("%d
    ", (ans.dis[st][ed] == 0x3f3f3f3f ? -1 : ans.dis[st][ed]));
        }
    }
    
  • 相关阅读:
    进制
    流程控制
    运算符
    格式化输出
    数据结构-树的遍历
    A1004 Counting Leaves (30分)
    A1106 Lowest Price in Supply Chain (25分)
    A1094 The Largest Generation (25分)
    A1090 Highest Price in Supply Chain (25分)
    A1079 Total Sales of Supply Chain (25分)
  • 原文地址:https://www.cnblogs.com/Zfio/p/13436186.html
Copyright © 2011-2022 走看看