P3247 [HNOI2016] 最小公倍数 题解
\(\text{P3247 [HNOI2016] 最小公倍数 题解}\)
第一眼上去没什么明显的思路。图上问题一般没有什么好的多项式复杂度算法来解决。观察数据范围,注意到 \(n,q\le 5\times 10^4\),是一个典型的根号复杂度算法,于是考虑分块来处理。注意到所求的不一定是简单路径,也就是在不超过所需要的 \(a,b\) 的情况下可以随便遍历。要分块处理,于是想到并查集维护连通性,顺手维护一下 \(a,b\) 的最大值。
现在考虑将它离线的具体方式:
注意到 \(a,b\) 的范围很大,因此对于询问分块的时间复杂度不太有保证。于是考虑对于边分块。我们将边按照 \(a\) 升序排序并分块,在解决每一块的问题时顺便揪出来和区间相关的询问。注意由于每一块之间的 \(a\) 取值可能重复,因此只需要揪出来最后一个使该区间为相关区间的询问,不然复杂度会爆炸。对于当前块外的部分,套路地用双指针维护,于是我们需要在一开始将询问按 \(b\) 排序,处理每一个块时将前面所有的块按照 \(b\) 排序,由于此时 \(a\) 已经合法,处理每个询问时双指针加入即可。对于块内的部分,每个询问遍历一次加入,结束询问后要撤销,因此用可撤销启发式合并的并查集。
现在分析复杂度。我们设块长为 \(k\):对于询问排序的复杂度总和是 \(O(q\log q)\),对于当前块之前的处理是 \(O(\dfrac{m^2\log m}{k})\),当前块内的处理显然是 \(qk\)。那么认为 \(\dfrac{m^2\log m}k=qk\) 时最小,此时 \(k=\sqrt{\dfrac{m^2\log m}{q}}\) 即可。但不知道为什么,事实上取 \(k=\sqrt{m\log q}\) 是最快的,或许是没有考虑启发式合并并查集的 \(\log k\) 吧。
代码:
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n, m;
struct DSU {
int fa[N], siz[N], mxa[N], mxb[N];
struct node {
int x, y, fx, fy, mxa, mxb, mya, myb;
};
node stk[N];
int top;
int fnd(int x) {
return x == fa[x] ? x : fnd(fa[x]);
}
void mge(int x, int y, int va, int vb) {
x = fnd(x), y = fnd(y);
if (siz[x] < siz[y]) swap(x, y);
stk[++top] = {x, y, fa[x], fa[y], mxa[x], mxb[x], mxa[y], mxb[y]};
fa[y] = x;
if (x != y) siz[x] += siz[y];
mxa[x] = max({mxa[x], mxa[y], va});
mxb[x] = max({mxb[x], mxb[y], vb});
}
void del(int t) {
while (1) {
node p = stk[top--];
int x = p.x, y = p.y;
if (x != y) siz[x] -= siz[y];
fa[x] = p.fx, fa[y] = p.fy;
mxa[x] = p.mxa, mxb[x] = p.mxb;
mxa[y] = p.mya, mxb[y] = p.myb;
if (top == t) return;
}
}
void init() {
for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1, mxa[i] = mxb[i] = -1;
top = 0;
}
} dsu;
struct node {
int x, y, a, b, id;
} e[N], q[N];
bool cmpa(node a, node b) {
return a.a < b.a;
}
bool cmpb(node a, node b) {
return a.b < b.b;
}
bool cmpe(int a, int b) {
return e[a].b < e[b].b;
}
bool cmpq(int a, int b) {
return q[a].b < q[b].b;
}
int B, bl[N], br[N], tot;
int ans[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
B = max((int)sqrt(m * log2(n)), 1), tot = m / B;
for (int i = 1; i <= tot; i++) bl[i] = br[i - 1] + 1, br[i] = bl[i] + B - 1;
if (br[tot] != m) {
++tot;
bl[tot] = br[tot - 1] + 1;
br[tot] = m;
}
for (int i = 1; i <= m; i++) cin >> e[i].x >> e[i].y >> e[i].a >> e[i].b;
sort(e + 1, e + 1 + m, cmpa);
int T;
cin >> T;
for (int i = 1; i <= T; i++) cin >> q[i].x >> q[i].y >> q[i].a >> q[i].b, q[i].id = i;
sort(q + 1, q + 1 + T, cmpb);
bl[tot + 1] = br[tot + 1] = N - 1;
e[N - 1] = {-1, -1, -1, -1};
for (int p = 1; p <= tot; p++) {
dsu.init();
vector<int>qu, egbf, egnw;
int la = e[bl[p]].a, ra = e[br[p]].a, lla = e[bl[p + 1]].a, rra = e[br[p + 1]].a;
for (int i = 1; i <= T; i++)
if ((la <= q[i].a && q[i].a <= ra) && !(lla <= q[i].a && q[i].a <= rra)) qu.push_back(i);
sort(qu.begin(), qu.end(), cmpq);
for (int i = 1; i < bl[p]; i++)
egbf.push_back(i);
for (int i = bl[p]; i <= br[p]; i++)
egnw.push_back(i);
sort(egbf.begin(), egbf.end(), cmpe);
sort(egnw.begin(), egnw.end(), cmpe);
int t = -1;
for (auto i : qu) {
while (t + 1 < (int)egbf.size() && q[i].b >= e[egbf[t + 1]].b) {
++t;
dsu.mge(e[egbf[t]].x, e[egbf[t]].y, e[egbf[t]].a, e[egbf[t]].b);
}
int nt = dsu.top;
for (auto j : egnw) {
if (q[i].a >= e[j].a && q[i].b >= e[j].b) dsu.mge(e[j].x, e[j].y, e[j].a, e[j].b);
}
int fx = dsu.fnd(q[i].x), fy = dsu.fnd(q[i].y);
if (fx == fy && q[i].a == dsu.mxa[fx] && q[i].b == dsu.mxb[fx])
ans[q[i].id] = 1;
if (dsu.top != nt) dsu.del(nt);
}
}
for (int i = 1; i <= T; i++) {
if (ans[i]) cout << "Yes\n";
else cout << "No\n";
}
return 0;
}

浙公网安备 33010602011771号