题意:
给定一张 n 个顶点 m 条边的无向图 ( 顶点编号为 1,2,…,n),每条边上带有权值。所有权值都可以分解成 2a×3b 的形式。
现在有 q 个询问,每次询问给定四个参数 u、v、a 和 b,请你求出是否存在一条顶点 u 到 v 之间的路径(不一定为简单路径),使得路径依次经过的边上的权值的最小公倍数为 2a×3b
1≤n,q≤50000、1≤m≤100000、0≤a,b≤109
题解:
显然询问的是是否存在一条路径 maxa=a,maxb=b。
因为不是简单路径,所以可以视作连通分量。
而且一个询问 Q 有解当且仅当将所有 a≤Qa,b≤Qb 的边相连,u, v 连通且 maxa=Qa,maxb=Qb。
因为没有更好的方法(233), 考虑分块。
二维分块一般按照一维分块 ( 排序后,每块 sz 个元素 ),块内按照第二维排序。
这里按照边 a 排序并分块。
分完块之后找到每一块 a 值的范围 [l,r]。有个细节,如果有一个 a 跨越了两个块,应该记在后面。将询问插入 a 值的相应块中。
考虑块 [l,r], 首先需要将 ai<l 的边按照 b 排序。对于每个询问 qj,在 ai<l 的边中找到 bi≤bqj 的边。因为已经按 b 递增,所以用一个指针扫过去,用并查集维护。
对于 ai∈[l,r] 的边,每次询问暴力扫一遍,将合法的插入并查集。因为完成每次询问后需要将它们清除,所以需要可撤销并查集(只按秩合并,不路径压缩,并记录操作,维护 maxa,maxb)。
复杂度 O(szlogn+sznlogn),但块大小不是设 n 最优。请自行尝试块大小。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| struct Edge { int u, v, a, b, id; }edge[MAXM]; int n, m, q; typedef Edge Query; Query query[MAXQ]; inline bool cmpa(Edge a, Edge b) { return a.a < b.a; } inline bool cmpb(Edge a, Edge b) { return a.b < b.b; } namespace solver1 { namespace dsu { int fa[MAXN], sz[MAXN]; int maxa[MAXN], maxb[MAXN]; typedef Edge Opt; Opt opt[MAXM]; int cnto; void init() { for (int i = 1; i <= n; i++) { fa[i] = i; maxa[i] = -1; maxb[i] = -1; sz[i] = 1; } } inline void reset() { cnto = 0; } inline int find(int x) { while(fa[x] != x) x = fa[x]; return x; } void unite(Edge e) { int u = find(e.u), v = find(e.v); if (sz[u] < sz[v]) std::swap(u, v); opt[++cnto] = (Opt) {u, v, maxa[u], maxb[u], sz[u]}; fa[v] = u; if (u != v) sz[u] += sz[v]; maxa[u] = std::max(maxa[u], std::max(maxa[v], e.a)); maxb[u] = std::max(maxb[u], std::max(maxb[v], e.b)); } void undo() { while(cnto) { int u = opt[cnto].u, v = opt[cnto].v, a = opt[cnto].a, b = opt[cnto].b; fa[v] = v; maxa[u] = a; maxb[u] = b; sz[u] = opt[cnto].id; cnto--; } } inline bool ok(Query e) { int u = find(e.u), v = find(e.v); if (u != v) return 0; return maxa[u] == e.a && maxb[u] == e.b; } } void main() { int block_sz = sqrt(m * log2(n)); std::sort(edge + 1, edge + m + 1, cmpa); std::sort(query + 1, query + q + 1, cmpa); static bool ans[MAXQ]; int curq = 1, lastq = 1; for (int l = 1; l <= m; l += block_sz) { int r = std::min(l + block_sz - 1, m); while(curq <= q && query[curq].a < edge[r].a) curq++; if (r == m) curq = q + 1; std::sort(edge + 1, edge + l, cmpb); std::sort(query + lastq, query + curq, cmpb); int taile = 1; dsu::init(); for (int j = lastq; j < curq; j++) { while(taile < l && edge[taile].b <= query[j].b) { dsu::unite(edge[taile++]); } dsu::reset(); for (int k = l; k <= r; k++) { if (edge[k].a <= query[j].a && edge[k].b <= query[j].b) { dsu::unite(edge[k]); } } ans[query[j].id] = dsu::ok(query[j]); dsu::undo(); } lastq = curq; } for (int i = 1; i <= q; i++) { puts(ans[i] ? "Yes" : "No"); } } }
|