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
题解:
考虑朴素做法,对于每个询问,把所有a,b值小于等于给定a,b的边合并.
顺便维护最大值,最后询问两点是否联通已经最大值是否正确即可.
这个显然可以用并查集来处理.
然后我们可以把边以a为第一关键字,b为第二关键字排序.再将边分块.
依次处理每个块,假设当前处理的块是x.
我们就处理a在这个块里的询问.把这些询问按b排序.
先把块1-(x-1)里符合条件的边加入.我们也可以对这些边按b排序,这样每条边只会加一次.
在块里也会有一些符合条件的边,我们暴力枚举这些边进行添加.
这些操作依然可以用并查集来处理,注意暴力枚举完我们还要暴力把并查集复原.
并查集不能路径压缩,使用启发式合并即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 50010
#define M 100010
using namespace std;
int n,m,fa[N],ans[N],bk,size[N],Q,ma[N],mb[N],cnt(0),num;
struct use{int x,y,a,b,id;}e[M],q[N],st[N];
struct aa{int f,x,y,size,ma,mb;}h[M];
int read(){
int x(0);char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
bool cmp1(use a,use b){
if (a.a==b.a) return a.b<b.b;
else return a.a<b.a;
}
bool cmp2(use a,use b){
if (a.b==b.b) return a.a<b.a;
else return a.b<b.b;
}
int find(int x){
if (x==fa[x]) return x;else return find(fa[x]);
}
void merge(int x,int y,int a,int b){
int r1=find(x),r2=find(y);
if (size[r1]>size[r2]) swap(r1,r2);
h[++num].x=r1;h[num].y=r2;h[num].f=fa[r1];
h[num].size=size[r2];h[num].ma=ma[r2];h[num].mb=mb[r2];
if (r1==r2){
ma[r1]=max(ma[r1],a);
mb[r1]=max(mb[r1],b);
}
else{
fa[r1]=r2;size[r2]+=size[r1];
ma[r2]=max(ma[r2],max(ma[r1],a));
mb[r2]=max(mb[r2],max(mb[r1],b));
}
}
void recovery(){
for (int i=num;i;i--){
fa[h[i].x]=h[i].f;
ma[h[i].y]=h[i].ma;
mb[h[i].y]=h[i].mb;
size[h[i].y]=h[i].size;
}
}
int main(){
n=read();m=read();
for (int i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
sort(e+1,e+m+1,cmp1);
bk=sqrt(m*log2(m));scanf("%d",&Q);
for (int i=1;i<=Q;i++)
q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
sort(q+1,q+Q+1,cmp2);
for (int i=1;i<=m;i+=bk){
cnt=0;
for (int j=1;j<=Q;j++)
if (q[j].a>=e[i].a&&(i+bk>m||q[j].a<e[i+bk].a))
st[++cnt]=q[j];
sort(e+1,e+i,cmp2);
for (int j=1;j<=n;j++) fa[j]=j,ma[j]=mb[j]=-1,size[j]=1;
int tot=1;
for (int j=1;j<=cnt;j++){
for(;tot<i&&e[tot].b<=st[j].b;tot++)
merge(e[tot].x,e[tot].y,e[tot].a,e[tot].b);
num=0;
for (int k=i;k<i+bk&&k<=m;k++)
if (e[k].a<=st[j].a&&e[k].b<=st[j].b)
merge(e[k].x,e[k].y,e[k].a,e[k].b);
int r1=find(st[j].x),r2=find(st[j].y);
if (r1==r2&&ma[r1]==st[j].a&&mb[r1]==st[j].b) ans[st[j].id]=1;
else ans[st[j].id]=0;
recovery();
}
}
for (int i=1;i<=Q;i++)
if (ans[i]) puts("Yes");
else puts("No");
}