• 4537: [Hnoi2016]最小公倍数


    4537: [Hnoi2016]最小公倍数

    链接

    题意:

      一张无向图,每条边有两个权值(a,b),多次询问是否存在一条从x到y的路径,使得路径上的最大值a和最大的b是给定的,路径可以不是简单路径。

    分析:

      考虑一次询问如何做,可以将所有小于等于a并且小于等b的边加入,并查集维护每个联通块的最大的a和最大的b,最后查询x,y坐在的连通块是否是一个,然后判断最大的a和最大的b是否是给定的即可。

      多次询问可以将边按照a分块,分成$sqrt m$块。对于一个询问,找到一个块,满足这一块的a小于等于它,下一块的a已经大于它了。那么前面的块的a一定都是满足的,于是对前面的块按b排序,询问也按b排序,依次加入b也满足的边即可。当然这一块内还有满足的a,边不大于$sqrt m$条,暴力加入,暴力撤销即可。并查集不能路径压缩,启发式合并。

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<cctype>
    #include<set>
    #include<queue>
    #include<vector>
    #include<map>
    using namespace std;
    typedef long long LL;
    
    inline int read() {
        int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
    }
    
    const int N = 200005;
    int fa[N], va[N], vb[N], ans[N], siz[N], opcnt;
    struct Data { int id, x, y, a, b; } e[N], q[N], sk[N];
    struct OPT { int x, y, fa, va, vb, siz; } op[N];
    inline bool cmpa(const Data &A,const Data &B) { 
        return A.a == B.a ? A.b < B.b : A.a < B.a;
    }
    inline bool cmpb(const Data &A,const Data &B) {
        return A.b == B.b ? A.a < B.a : A.b < B.b;
    }
    
    int find(int x) { return x == fa[x] ? x : find(fa[x]); } // 此处不要路径压缩!!! 
    void Merge(int x,int y,int a,int b) {
        x = find(x), y = find(y);
        if (siz[x] > siz[y]) swap(x, y);
        op[++opcnt] = (OPT){x, y, fa[x], va[y], vb[y], siz[y] };
        if (x == y) { 
            va[y] = max(va[y], a), vb[y] = max(vb[y], b); return ;
        }
        fa[x] = y, siz[y] += siz[x];
        va[y] = max(va[y], max(va[x], a));
        vb[y] = max(vb[y], max(vb[x], b));
    }
    void goback() {
        for (int i = opcnt; i; --i) {
            int x = op[i].x, y = op[i].y;
            fa[x] = op[i].fa; va[y] = op[i].va, vb[y] = op[i].vb, siz[y] = op[i].siz;
        }
        opcnt = 0;
    }
    int main() {
        int n = read(), m = read(), B = sqrt(m);
        for (int i = 1; i <= m; ++i)
            e[i].x = read(), e[i].y = read(), e[i].a = read(), e[i].b = read();
        int Q = read();
        for (int i = 1; i <= Q; ++i) 
            q[i].id = i, q[i].x = read(), q[i].y = read(), q[i].a = read(), q[i].b = read();
        sort(e + 1, e + m + 1, cmpa);    
        sort(q + 1, q + Q + 1, cmpb); 
        for (int i = 1; i <= m; i += B) {
            for (int j = 1; j <= n; ++j) fa[j] = j, va[j] = vb[j] = -1, siz[j] = 1;
            int top = 0;
            for (int j = 1; j <= Q; ++j) 
                if (q[j].a >= e[i].a && (i + B > m || q[j].a < e[i + B].a)) sk[++top] = q[j];
            sort(e + 1, e + i + 1, cmpb);
            for (int j = 1, k = 1; j <= top; ++j) {
                for (; k < i && e[k].b <= sk[j].b; ++k) 
                    Merge(e[k].x, e[k].y, e[k].a, e[k].b);
                opcnt = 0;
                for (int l = i; l < i + B && l <= m; ++l)
                    if (e[l].a <= sk[j].a && e[l].b <= sk[j].b) 
                        Merge(e[l].x, e[l].y, e[l].a, e[l].b);
                int x = find(sk[j].x), y = find(sk[j].y);
                ans[sk[j].id] = (x == y && va[x] == sk[j].a && vb[x] == sk[j].b);
                goback();
            }
        }
        for (int i = 1; i <= Q; ++i) puts(ans[i] ? "Yes" : "No");
        return 0;
    }
  • 相关阅读:
    Java实现 LeetCode 69 x的平方根
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 68 文本左右对齐
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 67 二进制求和
    Java实现 LeetCode 66 加一
    Java实现 LeetCode 66 加一
    CxSkinButton按钮皮肤类
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10474571.html
Copyright © 2020-2023  润新知