【NOI2014】魔法森林

Description

给定一张无向图,边有a,b两种边权,求一条1~n的路径,使得路径上a最大值与b最大值之和尽可能小

Solution

LCT维护生成树

将边按照a从小到大排序,然后顺序考虑每一条边

如果当前这条边的两个端点没有联通,那么直接在LCT上连边即可

如果当前这条边的两个端点已经连通,那么在LCT上找到这两点之间b值最大的一条边,若这条边的b值大于当前这条边那么将那条边断掉换成当前边

在任意时刻,若1与n联通,那么直接更新答案

Code

【NOI2014】魔法森林
  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 inline int read() {
  4     int ret = 0, op = 1;
  5     char c = getchar();
  6     while (!isdigit(c)) {
  7         if (c == '-') op = -1; 
  8         c = getchar();
  9     }
 10     while (isdigit(c)) {
 11         ret = (ret << 3) + (ret << 1) + c - '0';
 12         c = getchar();
 13     }
 14     return ret * op;
 15 }
 16 const int N = 200010;
 17 const int S = 131072;
 18 int n, m, s[N];
 19 struct Edge {
 20     int from, to, da, db;
 21     bool operator <(const Edge &x) const {
 22         return da < x.da;
 23     }
 24 } e[N];
 25 struct LCT {
 26     int fa, val, ch[2], maxx, tag;
 27 } a[N];
 28 inline int isnroot(int now) {
 29     return a[a[now].fa].ch[0] == now || a[a[now].fa].ch[1] == now;
 30 }
 31 inline void update(int now) {
 32     int l = a[now].ch[0];
 33     int r = a[now].ch[1];
 34     a[now].maxx = now;
 35     if (e[a[l].maxx].db > e[a[now].maxx].db) a[now].maxx = a[l].maxx;
 36     if (e[a[r].maxx].db > e[a[now].maxx].db) a[now].maxx = a[r].maxx;
 37 }
 38 inline void rev(int now) {
 39     swap(a[now].ch[0], a[now].ch[1]);
 40     a[now].tag ^= 1;
 41 }
 42 inline void pushdown(int now) {
 43     if (a[now].tag) {
 44         if (a[now].ch[0]) rev(a[now].ch[0]);
 45         if (a[now].ch[1]) rev(a[now].ch[1]);
 46         a[now].tag = 0;
 47     }
 48 }
 49 void rotate(int x) {
 50     int y = a[x].fa;
 51     int z = a[y].fa;
 52     int xson = a[y].ch[1] == x;
 53     int yson = a[z].ch[1] == y;
 54     int B = a[x].ch[xson ^ 1];
 55     if (isnroot(y)) a[z].ch[yson] = x;
 56     a[x].ch[xson ^ 1] = y;
 57     a[y].ch[xson] = B;
 58     if (B) a[B].fa = y;
 59     a[y].fa = x;
 60     a[x].fa = z;
 61     update(y);
 62 }
 63 void splay(int x) {
 64     int y = x, z = 0;
 65     s[++z] = y;
 66     while (isnroot(y)) y = a[y].fa, s[++z] = y;
 67     while (z) pushdown(s[z--]);
 68     while (isnroot(x)) {
 69         y = a[x].fa;
 70         z = a[y].fa;
 71         if (isnroot(y))
 72             (a[z].ch[0] == y) ^ (a[y].ch[0] == x) ? rotate(x) : rotate(y);
 73         rotate(x);
 74     }
 75     update(x);
 76 }
 77 void access(int x) {
 78     for (register int y = 0; x; y = x, x = a[x].fa) {
 79         splay(x); a[x].ch[1] = y; update(x);
 80     }
 81 }
 82 void makeroot(int x) {
 83     access(x);
 84     splay(x);
 85     rev(x);
 86 }
 87 int findroot(int x) {
 88     access(x); splay(x);
 89     while (a[x].ch[0]) pushdown(x), x = a[x].ch[0];
 90     return x;
 91 }
 92 void link(int i) {
 93     makeroot(e[i].to);
 94     a[e[i].to].fa = i;
 95     a[i].fa = e[i].from;
 96 }
 97 void cut(int i) {
 98     access(i); splay(i);
 99     a[a[i].ch[0]].fa = a[a[i].ch[1]].fa = 0;
100     a[i].ch[0] = a[i].ch[1] = 0;
101     update(i);
102 }
103 int main() {
104     n = read(); m = read();
105     for (register int i = 1; i <= m; ++i) {
106         e[i].from = read() | S; e[i].to = read() | S; e[i].da = read(); e[i].db = read();
107     }
108     sort(e + 1, e + m + 1);
109     int ans = 2147483647;
110     for (register int i = 1; i <= m; ++i) {
111         int x = e[i].from, y = e[i].to;
112         if (x == y) continue ;
113         makeroot(x);
114         if (x != findroot(y)) link(i);
115         else if (e[i].db < e[a[y].maxx].db) cut(a[y].maxx), link(i);
116         makeroot(1 | S);
117         if ((1 | S) == findroot(n | S)) ans = min(ans, e[i].da + e[a[n | S].maxx].db);
118     }
119     printf("%d\n", ans == 2147483647 ? -1 : ans);
120     return 0;
121 }
AC Code

 

上一篇:[NOI2014]魔法森林[最短路 spfa]


下一篇:「NOI2014」魔法森林