題解【LOJ3145】「APIO2019」橋樑
阿新 • • 發佈:2020-12-04
講一下部分分:
- 子任務 1:直接每次暴力做即可。
- 子任務 2:一條鏈,線段樹單點修改 + 維護區間最小值,加上二分。
- 子任務 3:不會。
- 子任務 4:沒有修改,維護原圖的一棵 Kruskal 重構樹即可。
- 子任務 5:不會。
接下來講正解。
發現這個資料範圍正好可以根號跑過,於是想到分塊。
將操作分塊,並將每一塊中的邊分成在塊中不會被修改和會被修改的兩類。
前一類邊可以直接按邊權從大到小加入,用並查集維護連通性和 \(size\)。
後一類邊我們可以對於每一次詢問暴力掃,看是否可以被加入,整個過程還需要使用可撤銷並查集。
具體實現細節見程式碼。
#include <bits/stdc++.h> #define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__) #define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout) using namespace std; typedef long long LL; typedef pair <int, int> PII; typedef pair <int, PII> PIII; template <typename T> inline T gi() { T f = 1, x = 0; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();} while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return f * x; } const int INF = 0x3f3f3f3f, N = 100003, M = N << 1; const int SZ = 500; int n, m, q; int tot; int fa[N], sz[N]; int nid[N]; //old id int val[N]; //edge's value int ans[N]; struct UnionFind {int u, v;} stk[N]; int tp; //a stack of dsu struct Node {int u, v, w, id;} a[M], p[M], g[M], tmp[M]; struct Query {int ty, x, y, id;} o[M], t1[M], t2[M]; bool vis[M]; //is modify? inline bool cmp(Node x, Node y) {return x.w == y.w ? x.id < y.id : x.w > y.w;} inline bool cmp1(Query x, Query y) {return x.y == y.y ? x.id < y.id : x.y > y.y;} int getf(int u) {return fa[u] == u ? u : getf(fa[u]);} inline void unionn(int u, int v) { int fu = getf(u), fv = getf(v); if (fu == fv) return; if (sz[fu] > sz[fv]) swap(fu, fv); stk[++tp] = {fu, fv}; fa[fu] = fv, sz[fv] += sz[fu]; } inline void Back() {int u = stk[tp].u, v = stk[tp].v; sz[v] -= sz[u], fa[u] = u, --tp;} inline void solve() { int tp1 = 0, tp2 = 0; tp = 0; for (int i = 1; i <= m; i+=1) vis[i] = false, nid[a[i].id] = i; for (int i = 1; i <= n; i+=1) sz[i] = 1, fa[i] = i; for (int i = 1; i <= tot; i+=1) if (o[i].ty == 1) vis[o[i].x] = true, t1[++tp1] = o[i]; else t2[++tp2] = o[i]; sort(t2 + 1, t2 + 1 + tp2, cmp1); int now = 1; for (int i = 1; i <= tp2; i+=1) { while (now <= m && a[now].w >= t2[i].y) { if (!vis[a[now].id]) unionn(a[now].u, a[now].v); ++now; } int nowtp = tp; for (int j = 1; j <= tp1; j+=1) val[t1[j].x] = a[nid[t1[j].x]].w; for (int j = 1; j <= tp1; j+=1) if (t1[j].id < t2[i].id) val[t1[j].x] = t1[j].y; for (int j = 1; j <= tp1; j+=1) if (val[t1[j].x] >= t2[i].y) unionn(a[nid[t1[j].x]].u, a[nid[t1[j].x]].v); ans[t2[i].id] = sz[getf(t2[i].x)]; while (tp > nowtp) Back(); } for (int i = 1; i <= tp1; i+=1) a[nid[t1[i].x]].w = t1[i].y; int nl = 0, nr = 0; for (int i = 1; i <= m; i+=1) if (vis[a[i].id]) p[++nl] = a[i]; else g[++nr] = a[i]; sort(p + 1, p + 1 + nl, cmp); int i = 1, j = 1, cnt = 0; while (i <= nl && j <= nr) if (p[i].w >= g[j].w) a[++cnt] = p[i++]; else a[++cnt] = g[j++]; while (i <= nl) a[++cnt] = p[i++]; while (j <= nr) a[++cnt] = g[j++]; return; } int main() { n = gi <int> (), m = gi <int> (); for (int i = 1; i <= m; i+=1) a[i].u = gi <int> (), a[i].v = gi <int> (), a[i].w = gi <int> (), a[i].id = i; sort(a + 1, a + 1 + m, cmp); q = gi <int> (); for (int i = 1; i <= q; i+=1) { ++tot; o[tot].ty = gi <int> (), o[tot].x = gi <int> (), o[tot].y = gi <int> (), o[tot].id = i; if (tot == SZ) solve(), tot = 0; } if (tot) solve(); for (int i = 1; i <= q; i+=1) if (ans[i]) printf("%d\n", ans[i]); return 0; }