• [NOI 2014]魔法森林


    Description

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

    魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

    只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

    由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

    Input

    第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

    Output

    输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

    Sample Input

    【输入样例1】
    4 5
    1 2 19 1
    2 3 8 12
    2 4 12 15
    1 3 17 8
    3 4 1 17
    【输入样例2】
    3 1
    1 2 1 1

    Sample Output

    【输出样例1】
    32
    【样例说明1】
    如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
    如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
    如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
    如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
    综上所述,小E最少需要携带32个守护精灵。
    【输出样例2】
    -1
    【样例说明2】
    小E无法从1号节点到达3号节点,故输出-1。

    HINT

    2<=n<=50,000

    0<=m<=100,000
    1<=ai ,bi<=50,000

    题解

    $LCT$ 维护边权信息的题...但据说动态 $SPFA$ 能水过,我这么菜,像这种 $NOI$ 的题也只能靠水才能做得了...

    贪心是把边按 $a$ 排序,动态加边,边权为 $b$ 。每加一条边,从边的两端点开始跑 $SPFA$ , $SPFA$ 维护路径上的瓶颈。然后取最后加的边的 $a$ 值 + $SPFA$ 过程中维护的起点到终点的最小化瓶颈值 $b$ 的和的最小值。

    注意的是动态 $SPFA$ 和 [HNOI 2014]道路堵塞 是一样的,不要去清空 $dist$ 数组。

     1 //It is made by Awson on 2018.1.10
     2 #include <set>
     3 #include <map>
     4 #include <cmath>
     5 #include <ctime>
     6 #include <queue>
     7 #include <stack>
     8 #include <cstdio>
     9 #include <string>
    10 #include <vector>
    11 #include <cstdlib>
    12 #include <cstring>
    13 #include <iostream>
    14 #include <algorithm>
    15 #define LL long long
    16 #define lowbit(x) ((x)&(-(x)))
    17 #define Max(a, b) ((a) > (b) ? (a) : (b))
    18 #define Min(a, b) ((a) < (b) ? (a) : (b))
    19 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
    20 using namespace std;
    21 const int N = 50000;
    22 const int M = 100000;
    23 const int INF = ~0u>>1;
    24 void read(int &x) {
    25     char ch; bool flag = 0;
    26     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    27     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    28     x *= 1-2*flag;
    29 }
    30 void write(int x) {
    31     if (x > 9) write(x/10);
    32     putchar(x%10+48);
    33 }
    34 
    35 int n, m, ans = INF;
    36 struct ss {
    37     int u, v, a, b;
    38     bool operator < (const ss &tmp) const {
    39     return a < tmp.a;
    40     }
    41 }e[M+5];
    42 struct tt {int to, next, cost; }edge[(M<<1)+5];
    43 int path[N+5], top;
    44 int q[N+5], head, tail, vis[N+5], dist[N+5];
    45 void add(int u, int v, int c) {edge[++top].to = v, edge[top].next = path[u], edge[top].cost = c, path[u] = top; }
    46 int SPFA(int u, int v) {
    47     vis[q[head = tail = 0] = u] = 1, ++tail, vis[q[tail] = v] = 1, ++tail;
    48     while (head != tail) {
    49     int u = q[head]; ++head, head %= N, vis[u] = 0;
    50     for (int i = path[u]; i; i = edge[i].next) {
    51         int v = edge[i].to;
    52         if (dist[v] > Max(dist[u], edge[i].cost)) {
    53         dist[v] = Max(dist[u], edge[i].cost);
    54         if (!vis[v]) vis[q[tail] = v] = 1, ++tail, tail %= N;
    55         }
    56     }
    57     }
    58     return dist[n];
    59 }
    60 void work() {
    61     read(n), read(m);
    62     for (int i = 1; i <= m; i++) read(e[i].u), read(e[i].v), read(e[i].a), read(e[i].b);
    63     sort(e+1, e+1+m); for (int i = 2; i <= n; i++) dist[i] = INF;
    64     for (int i = 1; i <= m; i++) {
    65     add(e[i].u, e[i].v, e[i].b), add(e[i].v, e[i].u, e[i].b);
    66     int tmp = SPFA(e[i].u, e[i].v); if (tmp != INF) ans = Min(ans, tmp+e[i].a);
    67     }
    68     if (ans != INF) write(ans);
    69     else putchar('-'), putchar('1');
    70 }
    71 int main() {
    72     work();
    73     return 0;
    74 }
    动态SPFA

    我已经这么菜了,肯定不能再怠惰...留个坑,用 $lct$ 再写一遍...

    upd 18.1.11:填坑,码完 $lct$ 了。

    对于边权问题的处理,用 $lct$ 不好简易地将边权转移为点权。解决这类问题的方法就是再开一倍点,将边变为点,表示边的点权就是原边权,而表示点的点权赋为 $0$ 。

    对于这道题,方法和上面的一样,按照 $a$ 排序, $lct$ 维护最小瓶颈路。

      1 //It is made by Awson on 2018.1.11
      2 #include <set>
      3 #include <map>
      4 #include <cmath>
      5 #include <ctime>
      6 #include <queue>
      7 #include <stack>
      8 #include <cstdio>
      9 #include <string>
     10 #include <vector>
     11 #include <cstdlib>
     12 #include <cstring>
     13 #include <iostream>
     14 #include <algorithm>
     15 #define LL long long
     16 #define Max(a, b) ((a) > (b) ? (a) : (b))
     17 #define Min(a, b) ((a) < (b) ? (a) : (b))
     18 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
     19 using namespace std;
     20 const int N = 100000;
     21 const int INF = ~0u>>1;
     22 
     23 int n, m, w[N+5], a[N+5], b[N+5], ans = INF;
     24 struct tt {
     25     int u, v, a, b;
     26     bool operator < (const tt &tmp) const {
     27     return a < tmp.a;
     28     }
     29 }e[N+5];
     30 struct Link_Cut_Tree {
     31     Link_Cut_Tree() {for (int i = 1; i <= N; i++) isrt[i] = 1; }
     32     int pre[N+5], ch[N+5][2], rev[N+5], maxi[N+5], isrt[N+5], pos;
     33     void pushup(int o) {
     34     if (!o) return;
     35     maxi[o] = o;
     36     if (w[maxi[ch[o][0]]] > w[maxi[o]]) maxi[o] = maxi[ch[o][0]];
     37     if (w[maxi[ch[o][1]]] > w[maxi[o]]) maxi[o] = maxi[ch[o][1]];
     38     }
     39     void pushdown(int o) {
     40     if (!rev[o] || !o) return;
     41     int ls = ch[o][0], rs = ch[o][1];
     42     Swap(ch[ls][0], ch[ls][1]), Swap(ch[rs][0], ch[rs][1]);
     43     rev[ls] ^= 1, rev[rs] ^= 1, rev[o] = 0;
     44     }
     45     void push(int o) {if (!isrt[o]) push(pre[o]); pushdown(o); }
     46     void rotate(int o, int kind) {
     47     int p = pre[o];
     48     ch[p][!kind] = ch[o][kind], pre[ch[o][kind]] = p;
     49     if (isrt[p]) isrt[o] = 1, isrt[p] = 0;
     50     else ch[pre[p]][ch[pre[p]][1] == p] = o;
     51     pre[o] = pre[p];
     52     ch[o][kind] = p, pre[p] = o;
     53     pushup(p), pushup(o);
     54     }
     55     void splay(int o) {
     56     push(o);
     57     while (!isrt[o]) {
     58         if (isrt[pre[o]]) rotate(o, ch[pre[o]][0] == o);
     59         else {
     60         int p = pre[o], kind = ch[pre[p]][0] == p;
     61         if (ch[p][kind] == o) rotate(o, !kind), rotate(o, kind);
     62         else rotate(p, kind), rotate(o, kind);
     63         }
     64     }
     65     }
     66     void access(int o) {
     67     int y = 0;
     68     while (o) {
     69         splay(o);
     70         isrt[ch[o][1]] = 1, isrt[ch[o][1] = y] = 0;
     71         pushup(o); o = pre[y = o];
     72         }
     73     }
     74     void makeroot(int o) {access(o), splay(o); rev[o] ^= 1, Swap(ch[o][0], ch[o][1]); }
     75     int find(int o) {access(o), splay(o); while (ch[o][0]) o = ch[o][0]; return o; }
     76     void link(int x, int y) {makeroot(x); pre[x] = y; }
     77     void cut(int x, int y) {makeroot(x); access(y), splay(y); ch[y][0] = pre[x] = 0, isrt[x] = 1; pushup(y); }
     78     int query(int x, int y) {
     79     if (find(x)^find(y)) return INF;
     80     makeroot(x), access(y), splay(y);
     81     return maxi[y];
     82     }
     83     void update(int x, int y, int c) {
     84     int last = query(x, y);
     85     if (last == INF) {w[++pos] = c, a[pos] = x, b[pos] = y; link(x, pos), link(y, pos); return; }
     86     if (w[last] <= c) return;
     87     cut(last, a[last]), cut(last, b[last]);
     88     w[last] = c, a[last] = x, b[last] = y; link(x, last), link(y, last);
     89     }
     90 }T;
     91 void work() {
     92     scanf("%d%d", &n, &m); T.pos = n;
     93     for (int i = 1; i <= m; i++) scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);
     94     sort(e+1, e+1+m);
     95     for (int i = 1; i <= m; i++) {
     96     T.update(e[i].u, e[i].v, e[i].b);
     97     int tmp = T.query(1, n); if (tmp != INF) ans = Min(ans, e[i].a+w[tmp]);
     98     }
     99     if (ans == INF) ans = -1; printf("%d
    ", ans);
    100 }
    101 int main() {
    102     work();
    103     return 0;
    104 }
    link-cut-tree
  • 相关阅读:
    电脑连不上网
    decompiler of java
    emmmmmmmmmmmmmmmmmm01
    WEB-INF
    tan?
    spring配置
    maven安装和使用前的几个点
    ※剑指offer系列29:两个链表的第一个公共结点
    剑指offer系列28:数组中的逆序对
    剑指offer系列27:第一个只出现一次的字符
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8261021.html
Copyright © 2020-2023  润新知