首页 > 其他 > 详细

【LG3247】[HNOI2016]最小公倍数

时间:2019-02-27 22:44:29      阅读:168      评论:0      收藏:0      [点我收藏+]

【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]最小公倍数

原文:https://www.cnblogs.com/heyujun/p/10447541.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!