本文版权归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
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 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
Yes
No
No
Yes
Yes
No
No
正解:分块+并查集
解题报告:
这道题从思想来上讲是一类分块优化枚举复杂度的好题。
这种两个限制条件的题目,我们惯用思路就是枚举一个,最优化另一个。
(以下排序皆为从小到大排序)考虑把所有边按a排序,所有询问按b排序。我把边分为根号块,显然,每个块的边的a是一个连续区间。
我每次处理一个块,处理这个块时,就把询问的a权值在这个区间的所有询问提出来处理。
与此同时,我把这个块之前的(不包括这个块)所有块的所有边提出来,按b排序,依次加入集合考虑。
考虑因为在这个块之前就意味着a对于我当前处理的所有询问一定是合法的,而此时边和询问都是按b排序的。
所以我可以单调指针往后扫,每次往并查集中加入b不超过当前询问限制的所有边,然后check一下,询问的两个点是否在一个连通块内,且连通块内的边权a、b最大值是否跟我求的一致。
但是上述做法还忽略了当前块的一些满足条件的边,我们可以对于每次询问暴力加入这一块中可以加入的所有边,再去check,做完这次询问再暴力恢复。
考虑我一个块内最多只有sqrt(m)个元素,所以复杂度可以保证,但是我的并查集就必须写按秩合并了,方便每次恢复。
上述做法就可以保证我查询的正确性了(有一个优化就是如果a大量相等,两个块之间就没必要重做了,可以留到下次),所以总复杂度:$O(msqrt{m}logm+qsqrt{m})$。
//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; }