[HNOI2016]最小公倍数

[HNOI2016]最小公倍数

分块,并查集

  1. 对边(a)和询问(b)分别排序
  2. 边分块处理,找出当前块内所有的询问一起处理
  3. 对于每一个块的询问:
    1. 对于前面块内的边,a肯定比当前询问小,所以按b排序,一条条加入并查集,直到超过询问的b(这一步可以双指针维护,就不用标记了)
    2. 暴力遍历该块,找到符合要求的边,加入并查集,同时标记
    3. 并查集判联通,最大值,最小值,得到询问答案
    4. 清空此次询问带标记的并查集操作(栈记录),进入下一个询问
  4. 清空并查集,进入下一个块

细节:

  • 初始化最大a、b赋为-1,因为 0a,b109
  • 边界处理,比如最后一个块

优化:

  • 发现瓶颈在于访问每个块时对前面的b排序,而前面大部分的b又是有序的,所以可以归并排序以降低时间复杂度
#include <bits/stdc++.h>
#define re register
// #define int long long
#define pair pair<int, int>
//#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
using namespace std;
inline int read()
{
    re int x = 0, f = 0;
    re char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = 1; ch = getchar();}
    while (isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
    return f ? -x : x;
}
inline string getstr()
{
    string res = "";
    re char ch = getchar();
    while (isspace(ch)) ch = getchar();
    while (!isspace(ch)) res.push_back(ch), ch = getchar();
    return res;
}
const int N = 5e4 + 4, M = 1e5 + 5;
int n, m, Q, ans[N];
struct edge
{
    int fr, to, a, b;
}e[M];
struct query
{
    int fr, to, a, b, id;
}q[N];
template<typename T> inline bool cmpa(const T &x, const T &y) {return x.a < y.a;}
template<typename T> inline bool cmpb(const T &x, const T &y) {return x.b < y.b;}
int fa[N], siz[N], mxa[N], mxb[N];
stack<tuple<int, int, int, int, int> > st;
inline void init()
{
    for (re int i = 1; i <= n; ++i)
        fa[i] = i, siz[i] = 1, mxa[i] = mxb[i] = -1;
}
inline int getfa(int u)
{
    while (fa[u] != u) u = fa[u];
    return u;
}
inline void Merge(int u, int v, const int &a, const int &b, const bool &del)
{
    u = getfa(u), v = getfa(v);
    if (siz[u] > siz[v]) swap(u, v);
    if (del) st.push(make_tuple(u, v, mxa[v], mxb[v], siz[v]));
    mxa[v] = max(mxa[v], a), mxb[v] = max(mxb[v], b);
    if (u == v) return;
    fa[u] = v, siz[v] += siz[u];
    mxa[v] = max(mxa[u], mxa[v]), mxb[v] = max(mxb[u], mxb[v]);
}
inline void delet()
{
    while (!st.empty())
    {
        int u, v, a, b, s;
        tie(u, v, a, b, s) = st.top(); st.pop();
        fa[u] = u, siz[v] = s, mxa[v] = a, mxb[v] = b;
    }
}
signed main()
{
    n = read(), m = read();
    for (re int i = 1; i <= m; ++i) e[i] = {read(), read(), read(), read()};
    Q = read();
    for (re int i = 1; i <= Q; ++i) q[i] = {read(), read(), read(), read(), i};
    sort(e + 1, e + m + 1, cmpa<edge>);
    sort(q + 1, q + Q + 1, cmpb<query>);
    const int len = sqrt(m * log2(n)); int siz = 0;
    static int *l = new int [m / len + 5], *v = new int [m + 5];
    for (re int i = 1; i <= m; i += len) l[++siz] = i;
    l[siz + 1] = m + 1, e[m + 1].a = 0x7fffffff, e[m + 1].b = 0x7fffffff;
    for (re int i = 1; i <= siz; ++i)
    {
        init();
        int cnt = 0;
        for (re int j = 1; j <= Q; ++j)
            if (q[j].a >= e[l[i]].a && q[j].a < e[l[i + 1]].a) v[++cnt] = j;
        if (!cnt) continue;
        sort(e + 1, e + l[i], cmpb<edge>);
        for (re int j = 1, k = 1; j <= cnt; ++j)
        {
            int now = v[j];
            while (k < l[i] && e[k].b <= q[now].b) Merge(e[k].fr, e[k].to, e[k].a, e[k].b, 0), ++k;
            for (re int x = l[i]; x < l[i + 1] && x <= m; ++x)
            {
                if (e[x].a > q[now].a) break;
                if (e[x].b <= q[now].b) Merge(e[x].fr, e[x].to, e[x].a, e[x].b, 1);
            }
            int u = getfa(q[now].fr), v = getfa(q[now].to);
            if (u == v && mxa[u] == q[now].a && mxb[u] == q[now].b) ans[q[now].id] = 1;
            delet();
        }
    }
    for (re int i = 1; i <= Q; ++i) puts(ans[i] ? "Yes" : "No");
    return 0;
}

本文作者:After-glow

本文链接:https://www.cnblogs.com/Aftglw/p/15836691.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   After-glow  阅读(64)  评论(0编辑  收藏  举报
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
点击右上角即可分享
微信分享提示