[AGC002D] Stamp Rally
阿新 • • 發佈:2021-12-17
\(\text{Solution}\)
很容易想到二分,然後 \(DFS\) 走所有能走的邊,看是否大於等於 \(z\)
這樣就是 \(O(nm \log m)\)
改進措施,思維不夠,暴力來湊
先改變下 \(check\)
把 \(\le mid\) 的邊加進圖中,並查集維護連通性和 \(size\)
直接判 \(x,y\) 的 \(size\) 即可
可二分,又多次,那就整體二分,可撤銷並查集處理下即可
\(O(m\log m\log n)\)
其實可以更簡單
仍舊二分,考慮一個點到另一個點會走的邊,顯然是最小生成樹上的邊
考慮 \(\text{Kruskal}\) 重構樹的性質,不難想到倍增來 \(check\)
\(O(m\log m\log n)\)
\(\text{Code}\)
#include <cstdio> #include <iostream> #define IN inline #define RE register using namespace std; const int N = 2e5 + 5, LG = 19; int n, m, q, f[N][LG], fa[N], val[N], h[N], tot, cnt[N]; struct edge{int to, nxt;}e[N]; IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;} IN void read(int &x) { x = 0; char ch = getchar(); for(; !isdigit(ch); ch = getchar()); for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar()); } int find(int x){return fa[x] == x ? x : (fa[x] = find(fa[x]));} void Dfs(int x) { if (x <= n) cnt[x] = 1; for(RE int i = 1; i < LG; i++) if (f[x][i - 1]) f[x][i] = f[f[x][i - 1]][i - 1]; else break; for(RE int i = h[x]; i; i = e[i].nxt) f[e[i].to][0] = x, Dfs(e[i].to), cnt[x] += cnt[e[i].to]; } IN void Kruskal() { int SIZE = n; for(RE int i = 1; i < n * 2; i++) fa[i] = i; for(RE int i = 1, x, y, u, v; i <= m; i++) read(x), read(y), u = find(x), v = find(y), (u ^ v) && (val[++SIZE] = i, add(SIZE, u), add(SIZE, v), fa[u] = fa[v] = SIZE); Dfs(SIZE); } IN int check(int mid, int x, int y, int z) { for(RE int i = LG - 1; i >= 0; i--) f[x][i] && (val[f[x][i]] <= mid) && (x = f[x][i]), f[y][i] && (val[f[y][i]] <= mid) && (y = f[y][i]); if (x == y) return (cnt[x] >= z); return (cnt[x] + cnt[y] >= z); } int main() { read(n), read(m), Kruskal(), read(q); for(RE int x, y, z, l, r, mid, ans; q; --q) { read(x), read(y), read(z), l = 1, r = m; for(mid = l + r >> 1; l <= r; mid = l + r >> 1) if (check(mid, x, y, z)) ans = mid, r = mid - 1; else l = mid + 1; printf("%d\n", ans); } }