洛谷传送门
BZOJ传送门(卡常, 慎入)
题目描述
SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从 x x 处送往 处,嘟嘟需要为供水公司找到一条从 A A 至 的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于 MY 市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将 MY 市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。
输入输出格式
输入格式:
输入文件第一行为 3 3 个整数: , M M , 分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
¥以下 M M 行,每行 个整数 x x , 和 t t ,描述一条对应的水管。 和 y y 表示水管两端结点的编号, 表示准备送水所需要的时间。我们不妨为结点从 1 1 至 编号,这样所有的 x x 和 都在范围 [1,N] [ 1 , N ] 内。
以下 Q Q 行,每行描述一项任务。其中第一个整数为 :
若 k = 1 k = 1 则后跟两个整数 A A 和 ,表示你需要为供水公司寻找一条满足要求的从 A A 到 的水管路径;
若 k = 2 k = 2 ,则后跟两个整数 x x 和 ,表示直接连接 x x 和 的水管宣布报废(保证合法,即在此之前直接连接 x x 和 尚未报废的水管一定存在)。
输出格式:
按顺序对应输入文件中每一项 k = 1 k = 1 的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。
输入输出样例
输入样例#1:
4 4 3
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4
输出样例#1:
2
3
说明
【约定】
○ N≤1000 N ≤ 1000
○ M≤100000 M ≤ 100000
○ Q≤100000 Q ≤ 100000
测试数据中宣布报废的水管不超过 5000 5000 条;且任何时候我们考虑的水管网络都是连通的,即从任一结点 A A 必有至少一条水管路径通往任一结点 。
注:此处数据为原题数据,原题数据不强(因为存在此题的数据加强版),故将时限由 3 3 秒改为 秒。
解题分析
考虑倒着加边, 我们的任务就成了维护两点之间边权最大值并使之尽量小, 所以用LCT维护连通性的方式即可。(可见这篇博客)
找一条边的编号可以用 map m a p ,但卡不进BZOJ的加强版的时限QAQ…
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 2000050
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
#define base 100005
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int dot, q, top, cnt, line, acnt;
int sta[MX], ans[MX], bel[MX];
bool vis[MX];
struct INFO {int a, b;};
IN bool operator < (const INFO &x, const INFO &y)
{return x.a == y.a ? x.b < y.b : x.a < y.a;}
std::map <INFO, int> mp;
struct Node
{
int son[2], mx, pos, fat, val;
bool rev;
}tree[MX];
struct Event
{
int typ, from, to, cost;
}eve[MX], pre[MX];
namespace DSU
{
void reset() {for (R int i = 1; i <= dot; ++i) bel[i] = i;}
int find(R int now) {return now == bel[now] ? now : bel[now] = find(bel[now]);}
}
namespace LCT
{
IN bool get(R int now) {return tree[dad].son[1] == now;}
IN bool nroot(R int now) {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
IN void pushup(R int now)
{
tree[now].mx = tree[now].val, tree[now].pos = now;
if(tree[ls].mx > tree[now].mx) tree[now].mx = tree[ls].mx, tree[now].pos = tree[ls].pos;
if(tree[rs].mx > tree[now].mx) tree[now].mx = tree[rs].mx, tree[now].pos = tree[rs].pos;
}
IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
IN void pushdown(R int now) {if(tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = false;}
IN void rotate(R int now)
{
R bool dir = get(now);
R int fa = dad, grand = tree[fa].fat;
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
if(nroot(fa)) tree[grand].son[get(fa)] = now;
tree[now].fat = grand;
tree[fa].fat = now;
tree[now].son[dir ^ 1] = fa;
pushup(fa);
}
IN void splay(R int now)
{
int tmp = now, fa;
sta[top = 1] = now;
W (nroot(now)) sta[++top] = now = dad;
W (top) pushdown(sta[top--]);
now = tmp;
W (nroot(now))
{
fa = dad;
if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
rotate(now);
}
pushup(now);
}
IN void access(R int now)
{
for (R int x = 0; now; x = now, now = dad)
{splay(now); rs = x; pushup(now);}
}
IN void makeroot(R int now)
{access(now), splay(now), pushrev(now);}
IN void split(R int x, R int y)
{makeroot(x); access(y); splay(y);}
IN void link(R int x, R int y)
{makeroot(x); tree[x].fat = y;}
IN void cut(R int x, R int y)
{split(x, y); tree[x].fat = tree[y].son[0] = 0; pushup(y);}
IN int findroot(R int now)
{
access(now); splay(now);
W (ls) pushdown(now), now = ls;
return now;
}
}
int main(void)
{
int id, tar;
in(dot), in(line), in(q);
DSU::reset();
for (R int i = 1; i <= line; ++i)
{
in(pre[i].from), in(pre[i].to), in(pre[i].cost);
tree[i + base].val = tree[i + base].mx = pre[i].cost;
tree[i + base].pos = i + base;
if(pre[i].from > pre[i].to) std::swap(pre[i].from, pre[i].to);
mp[{pre[i].from, pre[i].to}] = i;
}
for (R int i = 1; i <= q; ++i)
{
in(eve[i].typ);
in(eve[i].from), in(eve[i].to);
if(eve[i].from > eve[i].to) std::swap(eve[i].from, eve[i].to);
if(eve[i].typ == 2)
vis[mp[{eve[i].from, eve[i].to}]] = true;
}
for (R int i = 1; i <= line; ++i)
{
if(!vis[i])
{
if(DSU::find(pre[i].from) != DSU::find(pre[i].to))//并查集降低常数
LCT::link(pre[i].from, i + base), LCT::link(i + base, pre[i].to), bel[bel[pre[i].from]] = bel[pre[i].to];
else
{
LCT::split(pre[i].from, pre[i].to);
if(tree[pre[i].to].mx > pre[i].cost)
{
tar = tree[pre[i].to].pos - base;
LCT::cut(pre[tar].from, tar + base);
LCT::cut(tar + base, pre[tar].to);
LCT::link(pre[i].from, i + base);
LCT::link(i + base, pre[i].to);
}
}
}
}
for (R int i = q; i >= 1; --i)
{
if(eve[i].typ == 2)
{
LCT::split(eve[i].from, eve[i].to);
id = mp[{eve[i].from, eve[i].to}];
if(tree[eve[i].to].mx > pre[id].cost)
{
tar = tree[eve[i].to].pos - base;
LCT::cut(pre[tar].from, tar + base);
LCT::cut(tar + base, pre[tar].to);
LCT::link(eve[i].from, id + base);
LCT::link(id + base, eve[i].to);
}
}
else
{
LCT::split(eve[i].from, eve[i].to);
ans[++acnt] = tree[eve[i].to].mx;
}
}
W (acnt) printf("%d\n", ans[acnt--]);
}