首先声明,这份代码空间复杂度 \(O(n^2)\),瓶颈在给边打标记
由于博主太菜,懒得再改成低复杂度的打标记了,所以\(BZOJ\)的数据过不去
Description
给一张图,会有删边操作,求当前图路径\((x,y)\) 的最大边权
\(n \leq 10^3 ,m \le 10^5\)
或\(BZOJ\)版:\(n\le 10^5,m\le10^6\)
Solution
\[Begin\]
明确两点:
1.这个题要\(LCT\)(因为有删边的操作)
2.这个题把询问离线下来,然后逆序加边更可做
这个题中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\)就行了
\[Q.E.D\]
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\n", ans[i]); return 0; } } // namespace yspm signed main() { return yspm::main(); }
来源:https://www.cnblogs.com/yspm/p/12364992.html