4537: [Hnoi2016]最小公倍数
Time Limit: 40 Sec Memory Limit: 512 MB
Submit: 563 Solved: 236
Description
给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b
的形式。现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得
路径依次经过的边上的权值的最小公倍数为2^a*3^b。注意:路径可以不是简单路径。下面是一些可能有用的定义
:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数。路径:路径P:P1,P2,…,Pk是顶
点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
<=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。
Input
输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
b代表一条顶点u和v之间、权值为2^a*3^b的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9
Output
对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。
Sample Input
4 5
1 2 1 3
1 3 1 2
1 4 2 1
2 4 3 2
3 4 2 2
5
1 4 3 3
4 2 2 3
1 3 2 2
2 3 2 2
1 3 4 4
Sample Output
Yes
Yes
Yes
No
No
分块好题
问题转化为:每个边有两个权值a和b,每次询问两点之间是否存在一条路径满足路径上a的最大值和b的最大值分别等于一个数。
把边按照a排序,然后分块。
每次处理a的范围在当前块内的询问。假设处理到第i块,把询问和前i-1块按照b排序,这些部分O(n)就可以处理了。第i块里的边暴力处理,然后撤销修改。然后用一个并查集维护集合最大值。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define N 200005 #define inf 1000000000 using namespace std; int n,m,q,block,cnt,tot,f[N],sz[N],mxa[N],mxb[N]; bool ans[N]; struct data{int x,y,a,b,id;}a[N],b[N],c[N]; struct opt{int x,y,f,mxa,mxb,sz;}op[N]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } 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,int flg) { if (f[x]==x) return x; if (flg) op[++tot]=(opt){x,0,f[x],0,0,0}; return f[x]=find(f[x],flg); } void merge(int x,int y,int a,int b,int flg) { x=find(x,flg);y=find(y,flg); if (sz[x]<sz[y]) swap(x,y); if (flg) op[++tot]=(opt){x,y,f[y],mxa[x],mxb[x],sz[x]}; if (x==y){mxa[x]=max(mxa[x],a);mxb[x]=max(mxb[x],b);return;} f[y]=x;sz[x]+=sz[y]; mxa[x]=max(mxa[x],max(mxa[y],a)); mxb[x]=max(mxb[x],max(mxb[y],b)); } int main() { n=read();m=read();block=sqrt(m); F(i,1,m){a[i].x=read();a[i].y=read();a[i].a=read();a[i].b=read();a[i].id=i;} sort(a+1,a+m+1,cmpa); q=read(); F(i,1,q){b[i].x=read();b[i].y=read();b[i].a=read();b[i].b=read();b[i].id=i;} sort(b+1,b+q+1,cmpb); for(int i=1;i<=m;i+=block) { cnt=0; F(j,1,q) if (b[j].a>=a[i].a&&(i+block>m||b[j].a<a[i+block].a)) c[++cnt]=b[j]; sort(a+1,a+i,cmpb); F(j,1,n) f[j]=j,sz[j]=1,mxa[j]=mxb[j]=-inf; for(int j=1,k=1;j<=cnt;j++) { for(;k<i&&a[k].b<=c[j].b;k++) merge(a[k].x,a[k].y,a[k].a,a[k].b,0); tot=0; F(l,i,min(i+block-1,m)) if (a[l].a<=c[j].a&&a[l].b<=c[j].b) merge(a[l].x,a[l].y,a[l].a,a[l].b,1); int x=find(c[j].x,1),y=find(c[j].y,1); ans[c[j].id]=(x==y)&&(mxa[x]==c[j].a)&&(mxb[x]==c[j].b); D(l,tot,1) { if (!op[l].y) f[op[l].x]=op[l].f; else{f[op[l].y]=op[l].f;mxa[op[l].x]=op[l].mxa;mxb[op[l].x]=op[l].mxb;sz[op[l].x]=op[l].sz;} } } } F(i,1,q) puts(ans[i]?"Yes":"No"); return 0; }