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;
}

 

posted @ 2019-03-05 09:02  MJT12044  阅读(191)  评论(0编辑  收藏  举报