[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)
4537: [Hnoi2016]最小公倍数
Time Limit: 40 Sec Memory Limit: 512 MB
Submit: 1687 Solved: 607
[Submit][Status][Discuss]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^9Output
对于每次询问,如果存在满足条件的路径,则输出一行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 4Sample Output
Yes
Yes
Yes
No
NoHINT
Source
[Submit][Status][Discuss]
首先如果只有一个参数a,可以直接将边和询问排序然后扫一遍即可。
现在是二维偏序问题,我们就需要合理分块了。
将边按a排序,询问按b排序,考虑分块,每次找到所有第一关键字在[L,R]中的询问,那么我们将第一关键字在[1,L)的边按第二关键字排序,就可以指针扫一遍统计答案了,对于块内的问题,直接暴力合并和撤销并查集操作即可。
1 #include<cmath> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 using namespace std; 6 7 const int N=100100; 8 struct E{ int x,y,u,v,k; }a[N],b[N],c[N],h[N]; 9 void up(int &x,int y){ if (x<y) x=y; } 10 11 bool ans[N]; 12 int n,m,cnt,tot,fa[N],sz[N],fu[N],fv[N]; 13 bool cmpu(const E &p,const E &q){ return p.u<q.u || (p.u==q.u && p.v<q.v); } 14 bool cmpv(const E &p,const E &q){ return p.v<q.v || (p.v==q.v && p.u<q.u); } 15 int getfa(int x){ return (x==fa[x]) ? x : getfa(fa[x]); } 16 17 void merge(int x,int y,int u,int v){ 18 x=getfa(x); y=getfa(y); if (sz[x]>sz[y]) swap(x,y); 19 h[++tot]=(E){x,y,fu[y],fv[y],sz[y]}; 20 if (x!=y) fa[x]=y,sz[y]+=sz[x],up(fu[y],fu[x]),up(fv[y],fv[x]); 21 up(fu[y],u); up(fv[y],v); 22 } 23 24 int main(){ 25 freopen("bzoj4537.in","r",stdin); 26 freopen("bzoj4537.out","w",stdout); 27 scanf("%d%d",&n,&m); 28 rep(i,1,m) scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].u,&a[i].v); 29 sort(a+1,a+m+1,cmpu); scanf("%d",&cnt); 30 rep(i,1,cnt) scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].u,&b[i].v),b[i].k=i; 31 sort(b+1,b+cnt+1,cmpv); 32 int bl=sqrt(m); 33 for (int i=1; i<=m; i+=bl){ 34 rep(j,1,n) fa[j]=j,fu[j]=fv[j]=-1,sz[j]=1; 35 int len=0; 36 rep(j,1,cnt) if (b[j].u>=a[i].u && (i+bl>m || b[j].u<a[i+bl].u)) c[++len]=b[j]; 37 if (!len) continue; 38 if (i>1) sort(a+1,a+i,cmpv); 39 for (int j=1,k=1; j<=len; j++){ 40 for (; k<i && a[k].v<=c[j].v; k++) merge(a[k].x,a[k].y,a[k].u,a[k].v); 41 tot=0; 42 for (int l=i; l<i+bl && l<=m; l++) 43 if (a[l].u<=c[j].u && a[l].v<=c[j].v) merge(a[l].x,a[l].y,a[l].u,a[l].v); 44 int p=getfa(c[j].x),q=getfa(c[j].y); 45 ans[c[j].k]=(p==q && fu[p]==c[j].u && fv[p]==c[j].v); 46 for (; tot; tot--) p=h[tot].x,q=h[tot].y,fa[p]=p,fu[q]=h[tot].u,fv[q]=h[tot].v,sz[q]=h[tot].k; 47 } 48 } 49 rep(i,1,cnt) puts(ans[i]?"Yes":"No"); 50 return 0; 51 }