bzoj4537: [Hnoi2016]最小公倍数
Posted ccz181078
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4537: [Hnoi2016]最小公倍数相关的知识,希望对你有一定的参考价值。
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(注意:第一个字母大写,其余
字母小写) 。
存在合法路径等价于只保留a,b均不超过询问值的边,两点联通且a,b的最值分别能取到
对分块实现的莫队算法稍作修改就可以处理这个问题
将边按a分块,保证a相同的边分在同一块,且块大小尽可能在O(n/sqrt(m))
询问也分到对应的块中,对每个块,按b升序处理,左边的块内的边可以直接在并查集上修改,当前块内暴力修改和撤销
但若块大小因为a相同分到同一块的限制而较大则不能保证复杂度,对这种特殊情况,由于特殊性可以把当前块和左边的块内的边一起处理而不用撤销
总复杂度O(nsqrt(m)logn+mlongm)
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define G *++ptr const int N=50007; char buf[10000007],*ptr=buf-1; int _(){ int x=0,c=G; while(c<48)c=G; while(c>47)x=x*10+c-48,c=G; return x; } int n,m,q; bool as[N]; int f[N],xm[N],ym[N],B,bs[N],bp=0,*o1[N],o2[N],op=0; void undo(){ while(op)--op,*o1[op]=o2[op]; } inline void set(int*x,int y){ o1[op]=x; o2[op++]=*x; *x=y; } void init(){ memset(f,-1,sizeof(int)*(n+2)); memset(xm,-1,sizeof(int)*(n+2)); memset(ym,-1,sizeof(int)*(n+2)); } inline int gf(int x){ while(f[x]>0)x=f[x]; return x; } struct Q{ int u,v,x,y,id; void init(int i){ u=_();v=_();x=_();y=_(); id=i; } void cal(){ int a=gf(u),b=gf(v); if(a==b&&xm[a]==x&&ym[b]==y)as[id]=1; } }qs[N]; bool qcmpx(const Q&a,const Q&b){return a.x<b.x;} bool qcmpy(const Q&a,const Q&b){return a.y<b.y;} int max(int a,int b){return a>b?a:b;} struct edge{ int u,v,x,y; void init(){ u=_();v=_();x=_();y=_(); } void cal(){ int a=gf(u),b=gf(v); if(a!=b){ if(f[a]<f[b])std::swap(a,b); if(f[a]==f[b])--f[b]; f[a]=b; } int xx=max(xm[a],x),yy=max(ym[a],y); if(xx>xm[b])xm[b]=xx; if(yy>ym[b])ym[b]=yy; } void cal_(){ int a=gf(u),b=gf(v); if(a!=b){ if(f[a]<f[b])std::swap(a,b); if(f[a]==f[b])set(f+b,f[b]-1); set(f+a,b); } int xx=max(xm[a],x),yy=max(ym[a],y); if(xx>xm[b])set(xm+b,xx); if(yy>ym[b])set(ym+b,yy); } }e[N*2]; bool ecmpx(const edge&a,const edge&b){return a.x<b.x;} bool ecmpy(const edge&a,const edge&b){return a.y<b.y;} void msort(edge*l,edge*m,edge*r){ static edge ee[N*2]; std::sort(m,r,ecmpy); if(l==m||m==r)return; edge*p1=l,*p2=m,*p3=ee; while(p1!=m&&p2!=r)*p3++=p1->y<p2->y?*p1++:*p2++; while(p1!=m)*p3++=*p1++; while(p2!=r)*p3++=*p2++; memcpy(l,ee,(p3-ee)*sizeof(edge)); } int main(){ fread(buf,1,sizeof(buf),stdin)[buf]=0; n=_();m=_(); B=n/sqrt(m+1)*2+1; for(int i=0;i<m;++i)e[i].init(); q=_(); for(int i=0;i<q;++i)qs[i].init(i); std::sort(qs,qs+q,qcmpx); std::sort(e,e+m,ecmpx); for(int i=0,j=0;i<m;i=j){ for(;j<m&&e[i].x==e[j].x;++j); if(j-bs[bp]>B)bs[++bp]=i; } e[bs[++bp]=m].x=0x3f3f3f3f; for(int b=0,l=0,r=0,S=0;b<bp;++b){ int L=bs[b],R=bs[b+1]; for(l=r;r<q&&qs[r].x<e[R].x;++r); std::sort(qs+l,qs+r,qcmpy); if(e[L].x==e[R-1].x){ msort(e,e+S,e+R);S=R; init(); for(int ep=0,p=l;p<r;++p){ Q&w=qs[p]; for(;ep<R&&e[ep].y<=w.y;++ep)e[ep].cal(); w.cal(); } }else{ msort(e,e+S,e+L);S=L; init(); for(int ep=0,p=l;p<r;++p){ Q&w=qs[p]; for(;ep<L&&e[ep].y<=w.y;++ep)e[ep].cal(); for(int ep=L;ep<R;++ep)if(e[ep].x<=w.x&&e[ep].y<=w.y)e[ep].cal_(); w.cal(); undo(); } } } for(int i=0;i<q;++i)puts(as[i]?"Yes":"No"); return 0; }
以上是关于bzoj4537: [Hnoi2016]最小公倍数的主要内容,如果未能解决你的问题,请参考以下文章