BZOJ 4537: [Hnoi2016]最小公倍数
4537: [Hnoi2016]最小公倍数
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1084 Solved: 400
[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^9
Output
对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。
Sample Input
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
No
No
HINT
Source
分析:
貌似每次周末写题我都会产生一种强烈的我是一个智障的感觉...
如果考虑暴力的做法,显然是对于每个询问的$a,b$记录下来,然后把$a<=q.a$&&$b<=q.b$的边加入,用并查集维护连通性和联通快$ab$最大值,然后判断...
但是显然过不了...所以考虑分块,我们把边按照$a$排序,然后分块,再把询问按照$b$排序,枚举每一块,处理询问,取出当前块之前的边,按照$b$排序,然后对于询问把合法的边都加入用并查集维护起来,这个时候如果这条边合法,对于后面的询问也一定合法,接着考虑块内的边,因为$a$是无序的,所以我们必须要支持并查集的回溯操作,所以用一个栈记录并查集,然后对于块内的边每次用完就要回溯,因为要回溯,所以不能路径压缩,要用启发式合并...
我在回溯的时候,一开始写成了酱紫:
fa[x]=x,maxa[y]=a,maxb[y]=b,siz[y]-=siz[x];
然后T了一下午...
感谢RYC帮我查错...让我觉得我简直就是智障QAQ~~~
if(x!=y)fa[x]=x,siz[y]-=siz[x];
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> //by NeighThorn #define max(a,b) (a>b?a:b) using namespace std; const int maxn=50000+5,maxm=100000+5; int top,fa[maxn],siz[maxn],maxa[maxn],maxb[maxn]; int n,m,p,blo,id[maxm],be[maxn],en[maxn],ans[maxn]; struct M{ int x,y,a,b,num; }e[maxm],q[maxn],stk[maxn]; inline int read(void){ char ch=getchar();int x=0; while(!(ch>='0'&&ch<='9')) ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } inline bool cmpa(M xx,M yy){ if(xx.a!=yy.a) return xx.a<yy.a; return xx.b<yy.b; } inline bool cmpb(M xx,M yy){ if(xx.b!=yy.b) return xx.b<yy.b; return xx.a<yy.a; } inline int find(int x){ while(fa[x]!=x) x=fa[x]; return x; } inline void merge(int x,int y,int a,int b){ int fx=find(x),fy=find(y); if(siz[fx]>siz[fy]) swap(fx,fy); top++; stk[top].x=fx,stk[top].y=fy; stk[top].a=maxa[fy],stk[top].b=maxb[fy]; maxa[fy]=max(a,max(maxa[fx],maxa[fy])); maxb[fy]=max(b,max(maxb[fx],maxb[fy])); if(fx==fy) return; fa[fx]=fy;siz[fy]+=siz[fx]; } signed main(void){ n=read(),m=read();blo=sqrt(m*log2(m)); for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read(); for(int i=1;i<=m;i++) id[i]=(i-1)/blo+1; sort(e+1,e+m+1,cmpa);p=read(); for(int i=1;i<=id[m];i++) be[i]=lower_bound(id+1,id+m+1,i)-id, en[i]=upper_bound(id+1,id+m+1,i)-id-1; for(int i=1;i<=p;i++) q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].num=i; sort(q+1,q+p+1,cmpb); for(int i=1;i<=id[m];i++){ int st=1,A=e[be[i]].a,AA=e[en[i]+1].a; sort(e+1,e+be[i]+1,cmpb); for(int j=1;j<=n;j++) fa[j]=j,siz[j]=1,maxa[j]=maxb[j]=-1; for(int j=1;j<=p;j++) if(q[j].a>=A&&(i==id[m]||q[j].a<AA)){ for(;st<be[i]&&q[j].b>=e[st].b;st++) merge(e[st].x,e[st].y,e[st].a,e[st].b); top=0; for(int k=be[i];k<=en[i];k++) if(e[k].a<=q[j].a&&e[k].b<=q[j].b) merge(e[k].x,e[k].y,e[k].a,e[k].b); int fx=find(q[j].x),fy=find(q[j].y); ans[q[j].num]=(fx==fy&&maxa[fx]==q[j].a&&maxb[fx]==q[j].b); while(top){ int x=stk[top].x,y=stk[top].y; maxa[y]=stk[top].a,maxb[y]=stk[top].b; if(x!=y)fa[x]=x,siz[y]-=siz[x]; top--; } } } for(int i=1;i<=p;i++) puts(ans[i]?"Yes":"No"); return 0; }
By NeighThorn