给定一张 N NN 个顶点 M MM 条边的无向图(顶点编号为 1,2,⋯,n 1,2, \cdots ,n1,2,⋯,n),每条边上带有权值。全部权值均可以分解成 2a⋅3b 2^a \cdot 3^b2a⋅3b 的形式。html
如今有 q qq 个询问,每次询问给定四个参数 u uu、v vv、a aa 和 b bb,请你求出是否存在一条顶点 u uu 到 v vv 之间的路径,使得路径依次通过的边上的权值的最小公倍数为 2a⋅3b 2^a \cdot 3^b2a⋅3b 。node
注意:路径能够不是简单路径。ios
下面是一些可能有用的定义:
最小公倍数: k kk 个数 a1,a2,⋯,ak a_1 , a_2, \cdots , a_ka1,a2,⋯,ak 的最小公倍数是能被每一个 aia_iai 整除的最小正整数。
路径:路径 P:P1,P2,…,Pk 是顶点序列,知足对于任意 1≤i<k 1 \leq i < k1≤i<k ,节点 Pi P_iPi 和 Pi+1 P_{i+1}Pi+1 之间都有边相连。
简单路径:若是路径 P:P1,P2,…,Pk 中,对于任意 1≤s≠t≤k 1 \leq s \neq t \leq k1≤s≠t≤k 都有 Ps≠Pt P_s \neq P_tPs≠Pt ,那么称路径为简单路径。ide
输入文件的第一行包含两个整数 N NN 和 M MM ,分别表明图的顶点数和边数。
接下来 M MM 行,每行包含四个整数 u uu、v vv、a aa、b bb 表明一条顶点 u uu 和 v vv 之间、权值为 2a⋅3b 2^a \cdot 3^b2a⋅3b 的边。
接下来一行包含一个整数 q qq ,表明询问数。
接下来 q qq 行,每行包含四个整数 u uu 、v vv 、a aa 和 b bb,表明一次询问。
询问内容请参见问题描述。ui
对于每次询问,若是存在知足条件的路径,则输出一行 Yes
,不然输出一行 No
spa
(注意:第一个字母大写,其他字母小写) 。code
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
Yes Yes Yes No No
对于全部的数据,1≤n,q≤50000, 1≤m≤100000, 0≤a,b≤1091 \leq n,q \leq 50000,\ 1 \leq m \leq 100000,\ 0 \leq a,b \leq 10^91≤n,q≤50000, 1≤m≤100000, 0≤a,b≤109。htm
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 50010 using namespace std; int fa[maxn]; int n,m; struct node{ int u,v,a,b; bool operator < (const node &w) const { return a<w.a; } }E[100010]; int find(int x){ if(x==fa[x])return fa[x]; return fa[x]=find(fa[x]); } int main(){ scanf("%d%d",&n,&m); int x,y,a,b; for(int i=1;i<=m;i++) scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].a,&E[i].b); int q;scanf("%d",&q); sort(E+1,E+m+1); for(int Case=1;Case<=q;Case++){ for(int i=1;i<=n;i++)fa[i]=i; scanf("%d%d%d%d",&x,&y,&a,&b); if(x==y&&a==0&&b==0){puts("No");continue;} for(int i=1;i<=m;i++){ if(E[i].a>a)break; if(E[i].b>b)continue; int f1=find(E[i].u),f2=find(E[i].v); if(f1!=f2)fa[f1]=f2; } if(find(x)!=find(y)){puts("No");continue;} int mxa=0,mxb=0; for(int i=1;i<=m;i++){ if(E[i].a>a)break; if(E[i].b>b)continue; if(find(x)==find(E[i].v)&&find(x)==find(E[i].u)){ mxa=max(mxa,E[i].a); mxb=max(mxb,E[i].b); } } if(mxa==a&&mxb==b)puts("Yes"); else puts("No"); } return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100010 using namespace std; int n,m,block,Q,top,opcnt; int fa[maxn],mxa[maxn],mxb[maxn],ans[maxn],sz[maxn]; struct data{ int x,y,a,b,id; void init(int i){ id=i; scanf("%d%d%d%d",&x,&y,&a,&b); } bool operator < (const data &w)const{ if(a==w.a)return b<w.b; return a<w.a; } }e[maxn],q[maxn],st[maxn]; struct oper{int x,y,fa,mxa,mxb,sz;}op[maxn]; bool cmp(data x,data y){ if(x.b==y.b)return x.a<y.a; return x.b<y.b; } int find(int x){ if(x==fa[x])return fa[x]; return find(fa[x]); } void goback() { for (int i=opcnt; i; i--) { int x=op[i].x,y=op[i].y; fa[x]=op[i].fa; mxa[y]=op[i].mxa; mxb[y]=op[i].mxb; sz[y]=op[i].sz; } opcnt=0; } void merge(int x,int y,int a,int b) { x=find(x),y=find(y); if(sz[x]>sz[y])swap(x,y); op[++opcnt]=(oper){x,y,fa[x],mxa[y],mxb[y],sz[y]}; if(x==y){ mxa[y]=max(mxa[y],a),mxb[y]=max(mxb[y],b); return; } fa[x]=y,sz[y]+=sz[x]; mxa[y]=max(mxa[x],max(mxa[y],a)); mxb[y]=max(mxb[x],max(mxb[y],b)); } int main(){ scanf("%d%d",&n,&m); block=(int)sqrt(m); for(int i=1;i<=m;i++)e[i].init(i); sort(e+1,e+m+1); scanf("%d",&Q); for(int i=1;i<=Q;i++)q[i].init(i); sort(q+1,q+Q+1,cmp); for(int i=1;i<=m;i+=block){ top=0; for(int j=1;j<=Q;j++) if(q[j].a>=e[i].a && (i+block>m || q[j].a<e[i+block].a)) st[++top]=q[j]; sort(e+1,e+i+1,cmp); for(int j=1;j<=n;j++)fa[j]=j,mxa[j]=mxb[j]=-1,sz[j]=1; for(int j=1,k=1;j<=top;j++){ while(k<i&&e[k].b<=st[j].b){ merge(e[k].x,e[k].y,e[k].a,e[k].b); k++; } opcnt=0; for(int l=i;l<i+block && l<=m;l++) if(e[l].a<=st[j].a && e[l].b<=st[j].b) merge(e[l].x,e[l].y,e[l].a,e[l].b); int x=find(st[j].x),y=find(st[j].y); if(x==y && mxa[x]==st[j].a && mxb[x]==st[j].b)ans[st[j].id]=1; else ans[st[j].id]=0; goback(); } } for(int i=1;i<=Q;i++){ if(ans[i])puts("Yes"); else puts("No"); } return 0; }