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