本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
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
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXQ = 100011;
const int MAXM = 200011;
const int MAXS = 10011;
int n,m,Q,L[MAXS],R[MAXS],block,kcnt,belong[MAXM],top;
int maxa[MAXM],maxb[MAXM],father[MAXM],size[MAXM],cnt,ans[MAXQ];
struct edge{ int x,y,a,b; }e[MAXM];
inline bool cmpa(edge q,edge qq){ return q.a<qq.a; }
inline bool cmpb(edge q,edge qq){ return q.b<qq.b; }
struct ask{ int x,y,a,b,id; }q[MAXQ],tmp[MAXQ];
inline bool cmp1(ask q,ask qq){ return q.a<qq.a; }
inline bool cmp2(ask q,ask qq){ return q.b<qq.b; }
struct revor{ int x,y,size,fa,a,b; }c[MAXM];
inline int find(int x){ while(father[x]!=x) x=father[x]; return father[x]; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void add(int x,int y,int a,int b,int type){//type标记是否需要还原
int r1=find(x),r2=find(y); if(size[r1]>size[r2]) swap(r1,r2);
if(type==1) {
c[++top].x=r1; c[top].y=r2;
c[top].size=size[r2]; c[top].fa=r1; c[top].a=maxa[r2]; c[top].b=maxb[r2];//保存历史情况,便于恢复
}
if(r1==r2) maxa[r2]=max(maxa[r2],a),maxb[r2]=max(maxb[r2],b);
else {
father[r1]=r2;
size[r2]+=size[r1];
maxa[r2]=max(maxa[r2],max(maxa[r1],a));
maxb[r2]=max(maxb[r2],max(maxb[r1],b));
}
}
inline void work(){
n=getint(); m=getint(); for(int i=1;i<=m;i++) e[i].x=getint(),e[i].y=getint(),e[i].a=getint(),e[i].b=getint();
Q=getint(); for(int i=1;i<=Q;i++) q[i].x=getint(),q[i].y=getint(),q[i].a=getint(),q[i].b=getint(),q[i].id=i;
sort(e+1,e+m+1,cmpa); sort(q+1,q+Q+1,cmp2);//边按a排序,询问按b排序
block=(int)(sqrt(3*m)); kcnt=m/block; if(m%block) kcnt++;
int x,y; for(int i=1;i<=kcnt;i++) L[i]=m+1; //L初值是m+1不是n+1!!!
for(int i=1;i<=m;i++) belong[i]=(i-1)/block+1,L[belong[i]]=min(L[belong[i]],i),R[belong[i]]=i;
for(int i=1;i<=kcnt;i++) {
cnt=0;
for(int j=1;j<=Q;j++)
//大量a相等的块会留到下次处理
if(q[j].a>=e[L[i]].a && (R[i]==m || q[j].a<e[R[i]+1].a))//本次只处理询问的a处于当前块的所有询问
tmp[++cnt]=q[j];
sort(e+1,e+L[i],cmpb);
for(int j=1;j<=n;j++) father[j]=j,maxa[j]=maxb[j]=-1,size[j]=1;//size需要初始化!初值要设为-1!
for(int j=1,k=0;j<=cnt;j++) {
while(k+1<L[i] && e[k+1].b<=tmp[j].b) { k++; add(e[k].x,e[k].y,e[k].a,e[k].b,0); }//加入之前所有块中满足条件的所有边
for(int ii=L[i];ii<=R[i];ii++) //暴力加入这个块中的部分
if(e[ii].a<=tmp[j].a && e[ii].b<=tmp[j].b)
add(e[ii].x,e[ii].y,e[ii].a,e[ii].b,1);
x=tmp[j].x; y=tmp[j].y; x=find(x); y=find(y);
ans[tmp[j].id]=( (x==y) && (maxa[x]==tmp[j].a && maxb[x]==tmp[j].b) );
while(top>0) {//暴力消除这个块的部分的影响
x=c[top].x; y=c[top].y;
father[x]=c[top].fa;
size[y]=c[top].size;
maxa[y]=c[top].a;
maxb[y]=c[top].b;
top--;
}
}
}
for(int i=1;i<=Q;i++) if(ans[i]) puts("Yes"); else puts("No");
}
int main()
{
work();
return 0;
}