zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    2014-写给明年现在的自己
    DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射
    DDD 领域驱动设计-在动手之前,先把你的脑袋清理干净
    醍醐灌顶:领域驱动设计实现之路
    拨乱反正:DDD 回归具体的业务场景,Domain Model 再再重新设计
    设计窘境:来自 Repository 的一丝线索,Domain Model 再重新设计
    No zuo no die:DDD 应对具体业务场景,Domain Model 重新设计
    拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?
    【记录】GitHub/TortoiseGit 修改邮箱/提交者
    死去活来,而不变质:Domain Model(领域模型) 和 EntityFramework 如何正确进行对象关系映射?
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10474571.html
Copyright © 2011-2022 走看看