首先声明,这份代码空间复杂度 (O(n^2)),瓶颈在给边打标记
由于博主太菜,懒得再改成低复杂度的打标记了,所以(BZOJ)的数据过不去
Description
给一张图,会有删边操作,求当前图路径((x,y)) 的最大边权
(n leq 10^3 ,m le 10^5)
或(BZOJ)版:(nle 10^5,mle10^6)
Solution
这个题把询问离线下来,然后逆序加边,用 (LCT) 维护边权
这里有一个点边转化:
把每一个边拆成点,对端点连两条边(边权就是(e[i].dis)),这样就可以维护点权了,在(link)和(cut)操作中有点点不同
如下:
cut(e[tmp - n].from, tmp);
cut(e[tmp - n].to, tmp);
link(x[i], id[x[i]][y[i]] + n);
link(y[i], id[x[i]][y[i]] + n);
先给出图的最终态,然后求一个最小生成树
每个操作先打通链,也就是 (split)
然后对于询问操作,直接给(s[y])
对于改边的操作
每一次加边,都会在生成树上面添加一个环,然后我们找到环上最大边 (cut) 就可以
((split)已经通链了,所以直接断就可以)
然后(link)就行了
Code
不是很难写吧
#include <bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm {
inline int read() {
int res = 0, f = 1;
char k;
while (!isdigit(k = getchar()))
if (k == '-')
f = -1;
while (isdigit(k)) res = res * 10 + k - '0', k = getchar();
return res * f;
}
const int M = 1e6 + 10, N = 1e5 + 10;
int f[N], c[N][2], s[N], v[N], st[N], n, m, q, x[N], y[N], opt[N], fa[N], ans[N], id[2010][2010];
bool r[N];
struct node {
int from, to, dis, vis;
bool operator<(const node &a) const { return dis < a.dis; }
} e[M];
inline void push_up(int x) {
s[x] = max(s[c[x][0]], max(s[c[x][1]], v[x]));
return;
}
inline bool notroot(int x) { return c[f[x]][1] == x || c[f[x]][0] == x; }
inline void pushr(int x) {
swap(c[x][0], c[x][1]);
r[x] ^= 1;
return;
}
inline void push_down(int x) {
if (r[x]) {
if (c[x][0])
pushr(c[x][0]);
if (c[x][1])
pushr(c[x][1]);
}
return r[x] = 0, void();
}
inline void rotate(int tx) {
int ty = f[tx], z = f[ty], k = (c[ty][1] == tx), w = c[tx][!k];
if (notroot(ty))
c[z][c[z][1] == ty] = tx;
c[tx][!k] = ty;
c[ty][k] = w;
if (w)
f[w] = ty;
f[ty] = tx;
f[tx] = z;
return push_up(ty), push_up(tx);
}
inline void splay(int x) {
int y = x, z = 0;
st[++z] = y;
while (notroot(y)) st[++z] = y = f[y];
while (z) push_down(st[z--]);
while (notroot(x)) {
y = f[x], z = f[y];
if (notroot(y))
rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
rotate(x);
}
return push_up(y);
}
inline void access(int x) {
for (int y = 0; x; x = f[y = x]) splay(x), c[x][1] = y, push_up(x);
return;
}
inline void makeroot(int x) {
access(x);
splay(x);
pushr(x);
return;
}
inline int findroot(int x) {
access(x);
splay(x);
while (c[x][0]) push_down(x), x = c[x][0];
splay(x);
return x;
}
inline void split(int x, int y) {
makeroot(x);
access(y);
splay(y);
return;
}
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x)
f[x] = y;
return;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && f[y] == x && !c[y][0])
f[y] = c[x][1] = 0, push_up(x);
return;
}
inline int find(int x, int val) {
if (v[x] == val)
return x;
return s[c[x][0]] == val ? find(c[x][0], val) : find(c[x][1], val);
}
inline int getf(int x) { return x == fa[x] ? x : fa[x] = getf(fa[x]); }
inline void merge(int x, int y) { fa[getf(x)] = getf(y); }
inline void Kruskal() {
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
if (!e[i].vis && getf(e[i].from) != getf(e[i].to)) {
merge(e[i].from, e[i].to);
link(e[i].from, n + i);
link(e[i].to, n + i);
}
}
return;
}
signed main() {
n = read();
m = read();
q = read();
for (int i = 1; i <= m; ++i) e[i].from = read(), e[i].to = read(), e[i].dis = read();
sort(e + 1, e + m + 1);
for (int i = 1; i <= m; ++i) {
id[e[i].from][e[i].to] = id[e[i].to][e[i].from] = i;
v[n + i] = e[i].dis;
}
for (int i = 1; i <= q; ++i) {
opt[i] = read();
x[i] = read();
y[i] = read();
if (opt[i] == 2)
e[id[x[i]][y[i]]].vis = 1;
}
Kruskal();
int num = 0;
for (int i = q; i >= 1; --i) {
split(x[i], y[i]);
if (opt[i] == 1)
ans[++num] = s[y[i]];
else {
int tmp = find(y[i], s[y[i]]);
if (v[id[x[i]][y[i]] + n] < s[y[i]]) {
cut(e[tmp - n].from, tmp);
cut(e[tmp - n].to, tmp);
link(x[i], id[x[i]][y[i]] + n);
link(y[i], id[x[i]][y[i]] + n);
}
}
}
for (int i = num; i >= 1; --i) printf("%lld
", ans[i]);
return 0;
}
} // namespace yspm
signed main() { return yspm::main(); }