[HNOI2016]最小公倍数
题目描述
给定一张N个顶点M条边的无向图(顶点编号为1,2,...,n),每条边上带有权值。所有权值都可以分解成2a∗3b2^a*3^b2a∗3b 的形式。
现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2a∗3b2^a*3^b2a∗3b 。
注意:路径可以不是简单路径。下面是一些可能有用的定义:最小公倍数:K个数a1,a2,...,ak的最小公倍数是能被每个ai整除的最小正整数。
路径:路径P:P1,P2,...,Pk是顶点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,...,Pk中,对于任意1≤s≠t≤k1\le s\ne t\le k1≤s≠t≤k 都有Ps≠PtPs\ne PtPs≠Pt ,那么称路径为简单路径。
输入输出格式
输入格式:输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、b代表一条顶点u和v之间、权值为2a∗3b2^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
对于每个询问,我们可以把所有(a,b)小于等于的边加入并查集
并查集维护联通块的最大a和最大b,分别为Maxa,Maxb
如果最大(Maxa,Maxb)=(a,b)
暴力显然不行
现将边按a排序,分块
将询问按b排序
每一块加入a值在这一块的范围内的询问,即大于等于a[i]小于a[i+√m]
前面的块里的边显然a值都符合需求,按b排序
从前往后枚举加入的询问,将前面块里小于b的边加入
因为满足当前询问的边,必定也满足下一个询问,所以不需要撤销
然后枚举当前块里的边加入,处理完当前询问要撤销并查集的操作
因为撤销操作需要储存状态并复原,只有根号个撤销操作,复杂度有保证
不能用路径压缩
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct Node 8 { 9 int u,v,a,b,id; 10 }e[100001],q[50001],pre[100001],p[50001]; 11 int set[50001],tot,Maxa[50001],Maxb[50001],n,m,lim,r,cnt,ans[50001],size[50001]; 12 int gi() 13 { 14 char ch=getchar(); 15 int x=0; 16 while (ch<'0'||ch>'9') ch=getchar(); 17 while (ch>='0'&&ch<='9') 18 { 19 x=(x<<3)+(x<<1)+ch-'0'; 20 ch=getchar(); 21 } 22 return x; 23 } 24 bool cmpa(Node x,Node y) 25 { 26 if (x.a==y.a) return x.b<y.b; 27 return x.a<y.a; 28 } 29 bool cmpb(Node x,Node y) 30 { 31 if (x.b==y.b) return x.a<y.a; 32 return x.b<y.b; 33 } 34 int find(int x) 35 { 36 if (set[x]==x) return x; 37 if (set[x]!=x) return find(set[x]); 38 } 39 void merge(Node E) 40 { 41 int p=find(E.u),q=find(E.v); 42 if (size[p]<size[q]) swap(p,q); 43 ++tot; 44 pre[tot].u=p,pre[tot].v=q,pre[tot].a=Maxa[p],pre[tot].b=Maxb[p];pre[tot].id=size[p]; 45 if (p!=q) 46 { 47 set[q]=p; 48 size[p]+=size[q]; 49 } 50 if (Maxa[p]<E.a) 51 Maxa[p]=E.a; 52 if (Maxa[p]<Maxa[q]) 53 Maxa[p]=Maxa[q]; 54 if (Maxb[p]<E.b) 55 Maxb[p]=E.b; 56 if (Maxb[p]<Maxb[q]) 57 Maxb[p]=Maxb[q]; 58 } 59 void undo() 60 {int i; 61 Node E; 62 for (;tot;tot--) 63 { 64 E=pre[tot]; 65 set[E.v]=E.v; 66 Maxa[E.u]=E.a; 67 Maxb[E.u]=E.b; 68 size[E.u]=E.id; 69 } 70 } 71 int main() 72 {int i,j,k,l,ed,p1,p2; 73 cin>>n>>m; 74 lim=sqrt(m); 75 for (i=1;i<=m;i++) 76 { 77 e[i].u=gi();e[i].v=gi();e[i].a=gi();e[i].b=gi(); 78 e[i].id=i; 79 } 80 cin>>r; 81 for (i=1;i<=r;i++) 82 { 83 q[i].u=gi();q[i].v=gi();q[i].a=gi();q[i].b=gi(); 84 q[i].id=i; 85 } 86 sort(e+1,e+m+1,cmpa); 87 sort(q+1,q+r+1,cmpb); 88 for (i=1;i<=m;i+=lim) 89 { 90 cnt=0;tot=0; 91 k=1; 92 for (j=1;j<=r;j++) 93 if (q[j].a>=e[i].a&&(i+lim>m||q[j].a<e[i+lim].a)) 94 p[++cnt]=q[j]; 95 if (cnt==0) continue; 96 sort(e+1,e+i,cmpb); 97 if (i+lim-1<=m) ed=i+lim-1; 98 else ed=m; 99 for (j=1;j<=n;j++) 100 set[j]=j,Maxa[j]=-1,Maxb[j]=-1,size[j]=1; 101 for (j=1;j<=cnt;j++) 102 { 103 for (;k<i;k++) 104 if (p[j].b>=e[k].b) merge(e[k]); 105 else break; 106 tot=0; 107 for (l=i;l<=ed;l++) 108 if (p[j].b>=e[l].b&&p[j].a>=e[l].a) 109 { 110 merge(e[l]); 111 } 112 else if (e[l].a>p[j].a) break; 113 p1=find(p[j].u),p2=find(p[j].v); 114 if (p1==p2&&Maxa[p1]==p[j].a&&Maxb[p1]==p[j].b) 115 ans[p[j].id]=1; 116 undo(); 117 } 118 } 119 for (i=1;i<=r;i++) 120 if (ans[i]) 121 puts("Yes"); 122 else puts("No"); 123 }