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]最小公倍数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4537[Hnoi2016]最小公倍数 分块

bzoj4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数|分块

最小公倍数

[bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

BZOJ4540HNOI2016序列(莫队)