[BZOJ3732]Network
[BZOJ3732]Network
试题描述
给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).
现在有 K个询问 (1 < = K < = 15,000)。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?
输入
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。
第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?
输出
输入示例
6 6 8 1 2 5 2 3 4 3 4 3 1 4 8 2 5 7 4 6 2 1 2 1 3 1 4 2 3 2 4 5 1 6 2 6 1
输出示例
5 5 5 4 4 7 4 5
数据规模及约定
1 <= N <= 15,000
1 <= M <= 30,000
1 <= d_j <= 1,000,000,000
1 <= K <= 15,000
题解
一开始我自己yy了一个做法。考虑只有一个询问,那么显然是二分+并查集,然后我就想了一个能够“批处理”多组询问的方法(离线),就是对于答案区间 [al, ar],满足这个答案区间的操作区间为 [ql, qr],那么我们可以像二分那样搞一个 mid = (al + ar) / 2,然后对于 [ql, qr] 区间内的所有询问check一下,像归并排序那样把能够满足答案 mid 的询问放到左边,它们属于新的答案区间 [al, mid],不满足 mid 的询问放到右边,它们属于答案区间 [mid + 1, ar],然后递归处理就好了,边界是当 al = ar 时,所有在这个答案区间中的询问的答案显然都是 al (或 ar),当 ql > qr(即询问集合为空时)直接 return。后来发现这就是传说中的整体二分,看到自己能凭空yy出一个整体二分的方法感到很高兴,然而交上去便T了。。。搞下数据本地开 O2 测是快的飞起啊。。。。。
没办法上网搜一下题解,发现这TM不是 NOIP 题吗?!求一遍最小生成树再搞一个倍增 LCA 就好了。。。。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 15010 #define maxm 30010 #define maxq 20010 #define maxlog 15 int n, m, q; struct Edge { int a, b, c; bool operator < (const Edge& t) const { return c < t.c; } } es[maxm]; int pa[maxn]; int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); } int ToT, head[maxn], next[maxm], to[maxm], dist[maxm]; void AddEdge(int a, int b, int c) { to[++ToT] = b; dist[ToT] = c; next[ToT] = head[a]; head[a] = ToT; swap(a, b); to[++ToT] = b; dist[ToT] = c; next[ToT] = head[a]; head[a] = ToT; return ; } int dep[maxn], fa[maxn][maxlog], mxd[maxn][maxlog]; void build(int u) { for(int i = 1; i < maxlog; i++) { int a = fa[u][i-1]; fa[u][i] = fa[a][i-1]; mxd[u][i] = max(mxd[u][i-1], mxd[a][i-1]); } for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u][0]) { fa[to[e]][0] = u; mxd[to[e]][0] = dist[e]; dep[to[e]] = dep[u] + 1; build(to[e]); } return ; } int query(int a, int b) { if(dep[a] < dep[b]) swap(a, b); int ans = 0; for(int i = maxlog - 1; i >= 0; i--) if(dep[a] - (1 << i) >= dep[b]) ans = max(ans, mxd[a][i]), a = fa[a][i]; if(a == b) return ans; for(int i = maxlog - 1; i >= 0; i--) if(fa[a][i] != fa[b][i]) ans = max(ans, max(mxd[a][i], mxd[b][i])), a = fa[a][i], b = fa[b][i]; return max(ans, max(mxd[a][0], mxd[b][0])); } int main() { n = read(); m = read(); q = read(); for(int i = 1; i <= m; i++) es[i].a = read(), es[i].b = read(), es[i].c = read(); sort(es + 1, es + m + 1); for(int i = 1; i <= n; i++) pa[i] = i; for(int i = 1; i <= m; i++) { int u = findset(es[i].a), v = findset(es[i].b); if(u != v) { pa[v] = u; AddEdge(es[i].a, es[i].b, es[i].c); } } build(1); while(q--) { int a = read(), b = read(); printf("%d\n", query(a, b)); } return 0; }