【题目描述】
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
MST简单Kruskal即可,我选择了并查集维护,因为findroot效率远不如能完成同样工作的并查集。
题目保证link,cut合法,不需判断直接修改就好了。
一个坑点:加入新边修改MST时,一定要先把链上最长边先用变量保存下来!因为如果不保存的话link,cut操作在pushup的时候就会把最长边给修改掉,导致第二次cut的地方不正确。
Code:
#include
#include
#include
#include
#include
#include
#include
#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;
}