- 给定无向图 ((V,E)),每条边有两个边权 (a_i, b_i)。
- (q) 次操作,给出 (u_j, v_j, a_j, b_j),问是否存在 (u, v) 之间的路径使得两个边权的最大值分别是 (a_j, b_j)。
- (|V|, q leq 5 imes 10^4, |E| leq 10^5)。
类似[APIO2019]桥梁。
- 设 (N = q + |E|),边和操作和在一起都按 (a) 从大到小排序。
- 对于 (a_i geq max a_j) 的边 (i),和操作一起按照 (b_i) 从大到小排序,动态加边并查集维护联通块和联通块最大值。这部分复杂度是 (O(Nlog N))
- 对于 (min{a_j} leq a_i leq max{a_j}) 的边 (i),每次询问暴力访问,如果满足 (a_i geq a_j, b_i geq b_j) 就加入并查集。这部分是 (O(N^2log N))
分块处理,设块大小为 (S),第一部分不变,第二部分变为 (O(S^2 log S))。
总复杂度 (O(frac {N^2}Slog N + NSlog S))。
#include <bits/stdc++.h>
#define dbg(...) std::cerr << " 33[32;1m", fprintf(stderr, __VA_ARGS__), std::cerr << " 33[0m"
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(5e4 + 5), M(1e5 + 5), S(600);
struct Data {
int id, x, y, a, b;
bool operator<(const Data &r) const {
return a < r.a || a == r.a && id < r.id;
}
} a[M << 1];
int fa[N], siz[N], maxa[N], maxb[N], ta[N], tb[N], tc[N], td[N];
int find(int x) {
while (fa[x] != x) x = fa[x];
return x;
}
int ans[M];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, q;
std::cin >> n >> m;
for (int i = 1; i <= m; i++) {
std::cin >> a[i].x >> a[i].y >> a[i].a >> a[i].b;
}
std::cin >> q;
for (int i = 1; i <= q; i++) {
auto &[id, x, y, a, b] = ::a[i + m];
id = i;
std::cin >> x >> y >> a >> b;
}
m += q;
std::sort(a + 1, a + 1 + m);
for (int i = 1; i <= m; i += S) {
while (i <= m && !a[i].id) i++;
std::vector<int> b, c, d;
for (int j = 1; j < i; j++) if (!a[j].id) b.push_back(j), assert(a[j].a <= a[i].a);
auto cmp_b = [](int i, int j) { return a[i].b < a[j].b; };
std::sort(b.begin(), b.end(), cmp_b);
for (int j = 0; j < S && i + j <= m; j++) {
if (a[i + j].id) {
d.push_back(i + j);
} else {
c.push_back(i + j);
}
}
std::sort(d.begin(), d.end(), cmp_b);
std::iota(fa, fa + n + 1, 0);
std::fill(siz, siz + n + 1, 1);
std::fill(maxa, maxa + n + 1, -1);
std::fill(maxb, maxb + n + 1, -1);
auto p = b.begin();
for (int j : d) {
for (; p != b.end() && a[*p].b <= a[j].b; p++) {
int x = find(a[*p].x), y = find(a[*p].y);
if (x != y) {
if (siz[x] > siz[y]) std::swap(x, y);
fa[x] = y, siz[y] += siz[x];
smax(maxa[y], maxa[x]);
smax(maxb[y], maxb[x]);
}
smax(maxa[y], a[*p].a);
smax(maxb[y], a[*p].b);
}
std::vector<std::pair<int*, int>> v;
auto rec = [&](int &x) { v.emplace_back(&x, x); };
for (int k : c) {
if (a[k].a <= a[j].a && a[k].b <= a[j].b) {
int x = find(a[k].x), y = find(a[k].y);
if (siz[x] > siz[y]) std::swap(x, y);
rec(maxa[y]), rec(maxb[y]);
if (x != y) {
rec(fa[x]), rec(siz[y]);
fa[x] = y, siz[y] += siz[x];
smax(maxa[y], maxa[x]);
smax(maxb[y], maxb[x]);
}
smax(maxa[y], a[k].a);
smax(maxb[y], a[k].b);
}
}
int x = find(a[j].x), y = find(a[j].y);
assert(a[j].id);
ans[a[j].id] = x == y && maxa[x] == a[j].a && maxb[x] == a[j].b;
while (!v.empty()) {
auto &[x, y] = v.back();
*x = y;
v.pop_back();
}
}
}
for (int i = 1; i <= q; i++) {
std::cout << (ans[i] ? "Yes
" : "No
");
}
return 0;
}