[WC2006]水管局长——[LCT]

6 篇文章 0 订阅

【题目描述】

SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从 xx 处送往 yy 处,嘟嘟需要为供水公司找到一条从 AA 至 BB 的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。

在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于 MY 市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。

不妨将 MY 市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。

【输入格式】

输入文件第一行为 3 个整数:N, M, Q 分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。

以下 M 行,每行 3 个整数 x, y 和 t,描述一条对应的水管。x 和 y 表示水管两端结点的编号,t 表示准备送水所需要的时间。我们不妨为结点从 1 至 N 编号,这样所有的 x 和 y 都在范围[1,N]内。

以下 Q 行,每行描述一项任务。其中第一个整数为 k:

若 k=1 则后跟两个整数 A 和 B,表示你需要为供水公司寻找一条满足要求的从 A 到 B 的水管路径;

若 k=2,则后跟两个整数 x 和 y,表示直接连接 x 和 y 的水管宣布报废(保证合法,即在此之前直接连接 x 和 yy 尚未报废的水管一定存在)。

S a m p l e    I n p u t Sample~~Input Sample  Input

4 4 3
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4

S a m p l e    O u t p u t Sample~~Output Sample  Output

2
3

【题意分析】

这道吊题我调了整整一个晚上

用之前用过的trick:删边问题加边处理忘记哪位神仙讲的了,就是说,我们离线一下,从最终态的图出发,从后往前,就和电影倒带类似,水管报废就转化为水管修复。

题目保证任何时候图联通,那么我们可以搞一棵终态的MST,考虑加一条边时:

在MST中加一边必定会形成一个环,想一下加边的影响:比如说加入边(u,v),我们在MST上找到链(u->v),LCT维护链上最长边,如果新加的边比这条最大的还要大,那就不加,否则cut掉原边,link新边,相当于动态维护MST的形态。

由于需要快速获取每条边的信息,我们可以把每条边(u,v)严格转化为u<v的形式(简单点说,如果u>v,就交换一下)然后再用map映射一下(一个<pair,int>的map),这样就实现了边和数的迅速转化。

MST简单Kruskal即可,我选择了并查集维护,因为findroot效率远不如能完成同样工作的并查集。

题目保证link,cut合法,不需判断直接修改就好了。

一个坑点:加入新边修改MST时,一定要先把链上最长边先用变量保存下来!因为如果不保存的话link,cut操作在pushup的时候就会把最长边给修改掉,导致第二次cut的地方不正确。

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <map>
#include <algorithm>
#define MAXN 500000
using namespace std;
typedef pair <int, int> pii;

struct Node {
    int u, v, val;
}edge[MAXN], ques[MAXN];

map <pii, int> id;
int father[MAXN], son[MAXN][3], mx[MAXN], val[MAXN], ans[MAXN];
int stack[MAXN], a[MAXN], f[MAXN], tot, n, m, q;
bool broken[MAXN], rev[MAXN];

inline int read () {
    register int s = 0, w = 1;
    register char ch = getchar ();
    while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
    while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
    return s * w;
}

inline bool cmp (Node a, Node b) {return a.val < b.val;}

int getfather (int x) {
    return f[x] == x ? x : f[x] = getfather (f[x]);
}

inline void merge (int x, int y) {
    int xx = getfather (x), yy = getfather (y);
    f[yy] = xx;
}

struct lct {
    
    inline bool isroot (int x) {
        return ! (son[father[x]][0] == x || son[father[x]][1] == x);
    }
    
    inline void maintain (int x) {
        mx[x] = val[x];
        if (edge[mx[son[x][0]]].val > edge[mx[x]].val) mx[x] = mx[son[x][0]];
        if (edge[mx[son[x][1]]].val > edge[mx[x]].val) mx[x] = mx[son[x][1]];
    }
    
    inline void pushrev (int x) {
        swap (son[x][0], son[x][1]), rev[x] ^= 1;
    }
    
    inline void pushdown (int x) {
        if (rev[x]) {
            if (son[x][0]) pushrev (son[x][0]);
            if (son[x][1]) pushrev (son[x][1]);
            rev[x] ^= 1;
        }
    }
    
    inline void rotate (int x) {
        int y = father[x], z = father[y];
        int k = son[y][1] == x, kk = son[z][1] == y;
        if (! isroot (y)) son[z][kk] = x;
        father[x] = z;
        son[y][k] = son[x][k ^ 1];
        father[son[x][k ^ 1]] = y;
        son[x][k ^ 1] = y;
        father[y] = x;
        maintain (y), maintain (x);
    }
    
    inline void splay (int x) {
        int top = 0; stack[++top] = x;
        for (register int i = x; ! isroot (i); i = father[i]) stack[++top] = father[i];
        for (register int i = top; i; i--) pushdown (stack[i]);
        while (! isroot (x)) {
            int y = father[x], z = father[y];
            if (! isroot (y))
                (son[y][1] == x) ^ (son[z][1] == y)
                    ? rotate (x) : rotate (y);
            rotate (x);
        }
        maintain (x);
    }
    
    inline void access (int x) {for(int y=0;x;x=father[y=x])splay(x),son[x][1]=y,maintain(x);}
    inline void makeroot (int x) {access(x),splay(x),pushrev(x);}
    inline int findroot (int x) {access(x),splay(x);while(son[x][0])pushdown(x=son[x][0]);splay(x);return x;}
    inline void split (int x, int y) {makeroot(x),access(y),splay(y);}
    inline void link (int x, int y) {makeroot(x),father[x]=y;}
    inline void cut (int x, int y) {makeroot(x);if (findroot(y)==x&&father[y]==x&&!son[y][0])father[y]=son[x][1]=0;maintain(x);}
    
}tree;

int main () {
    n = read (), m = read (), q = read ();
    for (register int i = 1; i <= m; i++) {
        edge[i].u = read (), edge[i].v = read (), edge[i].val = read ();
        if (edge[i].u > edge[i].v) swap (edge[i].u, edge[i].v);
    }
    sort (edge + 1, edge + m + 1, cmp);
    for (register int i = 1; i <= m; i++) {
        pii o = make_pair (edge[i].u, edge[i].v); id[o] = i;
    }
    for (register int i = 1; i <= q; i++) {
        ques[i].val = read (), ques[i].u = read (), ques[i].v = read ();
        if (ques[i].u > ques[i].v) swap (ques[i].u, ques[i].v);
        if (ques[i].val == 2) {
            pii o = make_pair (ques[i].u, ques[i].v);
            a[i] = id[o], broken[id[o]] = 1;
        }
    }
    for (register int i = 1; i <= n; i++) f[i] = i;
    for (register int i = n + 1; i <= n + m; i++) mx[i] = val[i] = i - n;
    for (register int i = 1; i <= m; i++) {
        if (broken[i]) continue; if (tot == n - 1) break;
        if (getfather (edge[i].u) != getfather (edge[i].v)) {
            merge (edge[i].u, edge[i].v), tot++;
            tree.link (edge[i].u, i + n), tree.link (edge[i].v, i + n);
        }
    }
    for (register int i = q; i; i--) {
        int x = ques[i].u, y = ques[i].v;
        tree.split (x, y);
        if (ques[i].val == 1) ans[i] = edge[mx[y]].val;
        if (ques[i].val == 2) {
            int o = mx[y];
            if (edge[a[i]].val < edge[o].val) {
                tree.cut (edge[o].u, o + n), tree.cut (edge[o].v, o + n);
                tree.link (x, a[i] + n), tree.link (y, a[i] + n);
            }
        }
    }
    for (register int i = 1; i <= q; i++) if (ques[i].val == 1) printf ("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值