题目描述
给定一张N个顶点M条边的无向图(顶点编号为1,2,...,n),每条边上带有权值。所有权值都可以分解成2^a*3^b2a∗3b 的形式。
现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2^a*3^b2a∗3b 。
注意:路径可以不是简单路径。下面是一些可能有用的定义:最小公倍数:K个数a1,a2,...,ak的最小公倍数是能被每个ai整除的最小正整数。
路径:路径P:P1,P2,...,Pk是顶点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,...,Pk中,对于任意1\le s\ne t\le k1≤s≠t≤k 都有Ps\ne PtPs≠Pt ,那么称路径为简单路径。
输入输出格式
输入格式:输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、b代表一条顶点u和v之间、权值为2^a*3^b2a∗3b 的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9
对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余字母小写) 。
输入输出样例
输入样例#1:
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
输出样例#1:
Yes Yes Yes No No
分块+可回溯并查集
真tm恶心。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 50010
using namespace std;
int n,m,q,l,sum;
int fa[MAXN],size[MAXN],maxa[MAXN],maxb[MAXN];
bool ans[MAXN];
struct node{
int u,v,a,b,id;
}a[MAXN<<1],b[MAXN],c[MAXN];
struct node2{
int u,v,fa,size,maxa,maxb;
}h[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
bool cmpa(const node &x,const node &y){
if(x.a==y.a)return x.b<y.b;
return x.a<y.a;
}
bool cmpb(const node &x,const node &y){
if(x.b==y.b)return x.a<y.a;
return x.b<y.b;
}
int find(int x){return fa[x]==x?x:find(fa[x]);}
void uniun(int u,int v,int a,int b){
u=find(u);v=find(v);
if(size[u]>size[v])swap(u,v);
sum++;
h[sum].u=u;h[sum].v=v;h[sum].maxa=maxa[v];h[sum].maxb=maxb[v];
h[sum].fa=fa[u];h[sum].size=size[v];
if(u==v){
maxa[u]=max(maxa[u],a);
maxb[u]=max(maxb[u],b);
return;
}
fa[u]=v;
size[v]+=size[u];
maxa[v]=max(maxa[v],max(maxa[u],a));
maxb[v]=max(maxb[v],max(maxb[u],b));
}
void back(){
while(sum){
fa[h[sum].u]=h[sum].fa;
size[h[sum].v]=h[sum].size;
maxa[h[sum].v]=h[sum].maxa;
maxb[h[sum].v]=h[sum].maxb;
sum--;
}
}
void work(){
for(int i=1;i<=m;i+=l){
int top=0;
for(int j=1;j<=q;j++)if(b[j].a>=a[i].a&&(i+l>m||b[j].a<a[i+l].a))c[++top]=b[j];
sort(a+1,a+i,cmpb);
for(int j=1;j<=n;j++){
fa[j]=j;
size[j]=1;
maxa[j]=maxb[j]=-1;
}
for(int j=1,k=1;j<=top;j++){
for(;k<i&&a[k].b<=c[j].b;k++)uniun(a[k].u,a[k].v,a[k].a,a[k].b);
sum=0;
for(int p=i;p<i+l&&p<=m;p++)
if(a[p].a<=c[j].a&&a[p].b<=c[j].b)uniun(a[p].u,a[p].v,a[p].a,a[p].b);
int x=find(c[j].u),y=find(c[j].v);
ans[c[j].id]=(x==y&&maxa[x]==c[j].a&&maxb[x]==c[j].b);
back();
}
}
for(int i=1;i<=q;i++){
if(ans[i])printf("Yes\n");
else printf("No\n");
}
}
void init(){
n=read();m=read();
for(int i=1;i<=m;i++){
a[i].u=read();a[i].v=read();a[i].a=read();a[i].b=read();
a[i].id=i;
}
q=read();
for(int i=1;i<=q;i++){
b[i].u=read();b[i].v=read();b[i].a=read();b[i].b=read();
b[i].id=i;
}
sort(a+1,a+m+1,cmpa);
sort(b+1,b+q+1,cmpb);
l=sqrt(m);
}
int main(){
init();
work();
return 0;
}