• P3247 [HNOI2016]最小公倍数


    P3247 [HNOI2016]最小公倍数

    先考虑如果只有一个限制该怎么做。

    一个简单的思路就是离线下来,然后排序过后双指针扫描加边和处理询问即可,用并查集维护。

    或者每次询问的时候都暴力遍历所有边然后并查集。

    那么现在有了两个限制,单独并不好做

    考虑优化这个过程,我们可以先按 (a) 排序然后把询问分块,然后相当于对于一个块之前的所有边的 (a) 的相对大小都是固定下来的,那么对于这些边我们可以直接考虑对当前的块中询问做双指针,然后对于当前块的边,我们在每次询问的时候都枚举一遍所有边然后加入即可。

    每次操作完了之后要记得把当前的块中的边按 (a) 排序过后归并进边集合内。

    这需要可撤销并查集来维护,时间复杂度是 (O(nsqrt{n}logn))

    还可以继续优化。

    发现对于块内的边,我们可以直接BFS一遍,然后找到路径最大值就可以回答询问了,这里可以使用路径压缩按秩合并并查集,时间复杂度 (O(nsqrt{n}alpha(n)))

    同时,这道题的边要求其实就是一个二维偏序的要求,而我们想一下线段树分治的要求呢?——一维偏序。

    那么我们其实可以考虑使用 (KD-Tree) 套用线段树分治的思路,使用 (KD-Tree) 分治同样也可以解决这个问题,使用可撤销并查集维护,时间复杂度 (O(nlogn))

    代码:(可撤销并查集+分块)

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;char ch=getchar();bool f=false;
    	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    }
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    const int N=5e4+5,M=1e5+5,Q=1e3;
    struct line{
        int s,t,a,b,ans,no;
        friend bool operator < (line x,line y){
            return x.a<y.a;
        }
    }l[M],l2[M],q[N];
    inline bool cmp1(line x,line y){return x.a<y.a;}
    inline bool cmp2(line x,line y){return x.b<y.b;}
    inline bool cmp3(line x,line y){return x.no<y.no;}
    int n,m,K,block[Q];
    struct SIT{int type,x,num1,num2;}sta[N];
    int top;
    struct Unf{
        int fa[N],Siz[N],Maxa[N],Maxb[N];
        int FindFather(int x){
            if(fa[x]==0) return x;
            return FindFather(fa[x]);
        }
        void Merge(int x,int y,int a,int b,bool type){
            int fa1=FindFather(x),fa2=FindFather(y);
            if(Siz[fa1]>Siz[fa2]) swap(x,y),swap(fa1,fa2);
            if(type==1) sta[++top].type=1,sta[top].x=fa2,sta[top].num1=Maxa[fa2],sta[top].num2=Maxb[fa2];
            Maxa[fa2]=max(max(Maxa[fa2],Maxa[fa1]),a);
            Maxb[fa2]=max(max(Maxb[fa2],Maxb[fa1]),b);	
            if(fa1==fa2) return;
            if(type==1) sta[++top].type=0,sta[top].x=fa1,sta[top].num1=fa2,sta[top].num2=Siz[fa1];
            fa[fa1]=fa2,Siz[fa2]+=Siz[fa1];
        }
        void Return(){
            for(;top>0;top--){
                if(sta[top].type==0) fa[sta[top].x]=0,Siz[sta[top].num1]-=sta[top].num2;
                else Maxa[sta[top].x]=sta[top].num1,Maxb[sta[top].x]=sta[top].num2;
            }
        }
        int Query(int x,int y,int a,int b){
            if(x==y&&a==0&&b==0) return Siz[FindFather(x)]!=1;
            int fa=FindFather(x);
            if(FindFather(x)!=FindFather(y)) return false;
            if(Maxa[fa]!=a|Maxb[fa]!=b) return false;
            return true;
        }
    }Set[Q];
    int main(){
        read(n),read(m);
        for(int i=1;i<=m;i++) read(l[i].s),read(l[i].t),read(l[i].a),read(l[i].b),l2[i]=l[i];
        read(K);
        for(int i=1;i<=K;i++) read(q[i].s),read(q[i].t),read(q[i].a),read(q[i].b),q[i].no=i;
        int Siz=int(sqrt(m*20)),cnt=m/Siz;		
        for(int i=0;i<=cnt;i++) for(int j=1;j<=n;j++) Set[i].Siz[j]=1;
        sort(l+1,l+1+m,cmp1);
        sort(l2+1,l2+1+m,cmp1);
        for(int i=0;i<=m/Siz;i++) block[i]=l[i*Siz].a;
        sort(q+1,q+1+K,cmp2);
        sort(l+1,l+1+m,cmp2);
        int to=1;
        for(int i=1;i<=K;i++){
            for(;l[to].b<=q[i].b&&to<=m;to++){
                int begin=lower_bound(block,block+1+cnt,l[to].a)-block;
                for(int j=begin;j<=cnt;j++) Set[j].Merge(l[to].s,l[to].t,l[to].a,l[to].b,0);
            }
            int t=upper_bound(block,block+1+cnt,q[i].a)-block-1;
            line tmp;tmp.a=block[t];
            for(int j=upper_bound(l2+1,l2+1+m,tmp)-l2;j<=m&&l2[j].a<=q[i].a;j++) if(l2[j].b<=q[i].b) Set[t].Merge(l2[j].s,l2[j].t,l2[j].a,l2[j].b,1);
            q[i].ans=Set[t].Query(q[i].s,q[i].t,q[i].a,q[i].b);
            Set[t].Return();
        }
        sort(q+1,q+1+K,cmp3);
        for(int i=1;i<=K;i++) puts(q[i].ans==1?"Yes":"No");
        return 0;
    }
    
    
  • 相关阅读:
    从零开始在阿里云服务器(Ubuntu)上部署Rails应用
    rspec使用
    ubuntu安装和配置SVN
    给asp:DropDownList追加项到顶层显示
    js生成、删除表格方法
    js验证
    GridView和CheckBox连用,实现全选
    javascript;css;firefox;ie;区别
    回调示例
    GridView联合CheckBox实现全选功能[百度空间搜集]
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14665824.html
Copyright © 2020-2023  润新知