【LG3247】[HNOI2016]最小公倍數
阿新 • • 發佈:2019-02-28
兩種 gis const ios pre == 端點 mes ref
【LG3247】[HNOI2016]最小公倍數
題面
洛谷
題解
\(50pts\)
因為拼湊起來的部分分比較多,所以就放一起了。
以下設詢問的\(a,b\)為\(A,B\),
復雜度\(O(nm)\)的:將所有\(a\leq A,b\leq B\)的邊兩端,用並查集並起來,再看一看等於\(A,B\)的是否有端點在集合中即可。
一條鏈的:拿線段樹之類的數據結構維護一下即可。
\(a\)等於\(0\)的:將邊的和詢問按照\(b\)排序,用\(two\;pointers\)掃一遍丟到並查集中即可。
\(100pts\)
首先考慮暴力,每次詢問暴力求出所有\(\leq a, \leq b\)的邊,然後判斷判斷兩點是否聯通,並且聯通塊內最大值是否合法就可以了。
接下來的\(A\)和\(B\)還是是詢問的\(a, b\)。
將所有的邊按照\(a\)排序並分塊,將所有的詢問按照\(b\)排序。
設第\(i\)塊的區間是\([l_i, r_i]\),找出所有的\(A \in [a_{l_i}, a_{r_i})\)的詢問,然後一個一個處理。
對於第\(j\)個詢問,有兩種邊可以產生貢獻,一種是在\([1, i)\)的\(b \leq B_j\)的邊,這種邊可以用一個指針維護。
還有一種是在第\(i\)塊的\(a \leq A_j\),\(b \leq B_j\)的邊,這種邊最多只有塊的大小條,可以暴力加邊。
因為每一次加了第二種邊之後要撤銷這些操作,所以要寫一個支持撤銷的並查集。
然後,當塊的大小為\(\sqrt{m\log_2n}\)時據說最快。
(感謝\(xgzc\)友情提供)
代碼
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; inline int gi() { register int data = 0, w = 1; register char ch = 0; while (!isdigit(ch) && ch != '-') ch = getchar(); if (ch == '-') w = -1, ch = getchar(); while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); return w * data; } const int MAX_N = 2e5 + 5; struct Edge { int u, v, a, b; } e[MAX_N]; struct Query { int u, v, a, b, id; } q[MAX_N], p[MAX_N]; struct Node { int u, v, a, b, s; } stk[MAX_N]; bool cmp1(const Edge &l, const Edge &r) { return l.a == r.a ? l.b < r.b : l.a < r.a; } bool cmp2(const Edge &l, const Edge &r) { return l.b == r.b ? l.a < r.a : l.b < r.b; } bool cmp3(const Query &l, const Query &r) { return l.b == r.b ? l.a < r.a : l.b < r.b; } int N, M, Q, top, ans[MAX_N], fa[MAX_N], A[MAX_N], B[MAX_N], size[MAX_N]; int getf(int x) { return fa[x] == x ? x : getf(fa[x]); } void merge(int x, int y, int a, int b) { x = getf(x), y = getf(y); if (size[x] > size[y]) swap(x, y); stk[++top] = (Node){x, y, A[y], B[y], size[y]}; if (x != y) fa[x] = y, size[y] += size[x], A[y] = max(A[x], A[y]), B[y] = max(B[x], B[y]); A[y] = max(A[y], a); B[y] = max(B[y], b); } int main() { #ifndef ONLINE_JUDGE freopen("cpp.in", "r", stdin); #endif N = gi(), M = gi(); for (int i = 1; i <= M; i++) e[i] = (Edge){gi(), gi(), gi(), gi()}; Q = gi(); for (int i = 1; i <= Q; i++) q[i] = (Query){gi(), gi(), gi(), gi(), i}; sort(&e[1], &e[M + 1], cmp1); sort(&q[1], &q[Q + 1], cmp3); for (int i = 1, LEN = sqrt(M * log2(N)); i <= M; i += LEN) { for (int j = 1; j <= N; j++) size[fa[j] = j] = 1, A[j] = B[j] = -1; int tot = 0; for (int j = 1; j <= Q; j++) if (e[i].a <= q[j].a && (i + LEN > M || q[j].a < e[i + LEN].a)) p[++tot] = q[j]; if (!tot) continue; sort(&e[1], &e[i], cmp2); for (int j = 1, k = 1; j <= tot; j++) { while (k < i && e[k].b <= p[j].b) merge(e[k].u, e[k].v, e[k].a, e[k].b), ++k; top = 0; for (int l = i; l < i + LEN && l <= M; l++) if (e[l].a <= p[j].a && e[l].b <= p[j].b) merge(e[l].u, e[l].v, e[l].a, e[l].b); int x = getf(p[j].u), y = getf(p[j].v); ans[p[j].id] = (x == y && A[x] == p[j].a && B[x] == p[j].b); while (top) { int x = stk[top].u, y = stk[top].v; fa[x] = x; A[y] = stk[top].a, B[y] = stk[top].b, size[y] = stk[top].s; --top; } } } for (int i = 1; i <= Q; i++) puts(ans[i] ? "Yes" : "No"); return 0; }
【LG3247】[HNOI2016]最小公倍數