给定一张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,那么称路径为简单路径。
输入文件的第一行包含两个整数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
对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。
如果考虑暴力的做法,显然是对于每个询问的$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了一下午...
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;
}