• BZOJ 4537: [Hnoi2016]最小公倍数


    4537: [Hnoi2016]最小公倍数

    Time Limit: 40 Sec  Memory Limit: 512 MB
    Submit: 1084  Solved: 400
    [Submit][Status][Discuss]

    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

    Sample Output

    Yes
    Yes
    Yes
    No
    No

    HINT

    Source

    分析:

    貌似每次周末写题我都会产生一种强烈的我是一个智障的感觉...

    如果考虑暴力的做法,显然是对于每个询问的$a,b$记录下来,然后把$a<=q.a$&&$b<=q.b$的边加入,用并查集维护连通性和联通快$ab$最大值,然后判断...

    但是显然过不了...所以考虑分块,我们把边按照$a$排序,然后分块,再把询问按照$b$排序,枚举每一块,处理询问,取出当前块之前的边,按照$b$排序,然后对于询问把合法的边都加入用并查集维护起来,这个时候如果这条边合法,对于后面的询问也一定合法,接着考虑块内的边,因为$a$是无序的,所以我们必须要支持并查集的回溯操作,所以用一个栈记录并查集,然后对于块内的边每次用完就要回溯,因为要回溯,所以不能路径压缩,要用启发式合并...

    我在回溯的时候,一开始写成了酱紫:

    fa[x]=x,maxa[y]=a,maxb[y]=b,siz[y]-=siz[x];
    

      

    然后T了一下午...

    感谢RYC帮我查错...让我觉得我简直就是智障QAQ~~~

    if(x!=y)fa[x]=x,siz[y]-=siz[x];
    

      

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    //by NeighThorn
    #define max(a,b) (a>b?a:b)
    using namespace std;
     
    const int maxn=50000+5,maxm=100000+5;
     
    int top,fa[maxn],siz[maxn],maxa[maxn],maxb[maxn];
    int n,m,p,blo,id[maxm],be[maxn],en[maxn],ans[maxn];
     
    struct M{
        int x,y,a,b,num;
    }e[maxm],q[maxn],stk[maxn];
     
    inline int read(void){
        char ch=getchar();int x=0;
        while(!(ch>='0'&&ch<='9')) ch=getchar();
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    inline bool cmpa(M xx,M yy){
        if(xx.a!=yy.a) return xx.a<yy.a;
        return xx.b<yy.b;
    }
     
    inline bool cmpb(M xx,M yy){
        if(xx.b!=yy.b) return xx.b<yy.b;
        return xx.a<yy.a;
    }
     
    inline int find(int x){
        while(fa[x]!=x) x=fa[x];
        return x;
    }
     
    inline void merge(int x,int y,int a,int b){
        int fx=find(x),fy=find(y);
        if(siz[fx]>siz[fy])
            swap(fx,fy);
        top++;
        stk[top].x=fx,stk[top].y=fy;
        stk[top].a=maxa[fy],stk[top].b=maxb[fy];
        maxa[fy]=max(a,max(maxa[fx],maxa[fy]));
        maxb[fy]=max(b,max(maxb[fx],maxb[fy]));
        if(fx==fy) return;
        fa[fx]=fy;siz[fy]+=siz[fx];
    }
     
    signed main(void){
        n=read(),m=read();blo=sqrt(m*log2(m));
        for(int i=1;i<=m;i++)
            e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
        for(int i=1;i<=m;i++) id[i]=(i-1)/blo+1;
        sort(e+1,e+m+1,cmpa);p=read();
        for(int i=1;i<=id[m];i++)
            be[i]=lower_bound(id+1,id+m+1,i)-id,
            en[i]=upper_bound(id+1,id+m+1,i)-id-1;
        for(int i=1;i<=p;i++)
            q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].num=i;
        sort(q+1,q+p+1,cmpb);
        for(int i=1;i<=id[m];i++){
            int st=1,A=e[be[i]].a,AA=e[en[i]+1].a;
            sort(e+1,e+be[i]+1,cmpb);
            for(int j=1;j<=n;j++) 
                fa[j]=j,siz[j]=1,maxa[j]=maxb[j]=-1;
            for(int j=1;j<=p;j++)
                if(q[j].a>=A&&(i==id[m]||q[j].a<AA)){
                    for(;st<be[i]&&q[j].b>=e[st].b;st++)
                        merge(e[st].x,e[st].y,e[st].a,e[st].b);
                    top=0; 
                    for(int k=be[i];k<=en[i];k++)
                        if(e[k].a<=q[j].a&&e[k].b<=q[j].b)
                            merge(e[k].x,e[k].y,e[k].a,e[k].b);
                    int fx=find(q[j].x),fy=find(q[j].y);
                    ans[q[j].num]=(fx==fy&&maxa[fx]==q[j].a&&maxb[fx]==q[j].b);
                    while(top){
                        int x=stk[top].x,y=stk[top].y;
                        maxa[y]=stk[top].a,maxb[y]=stk[top].b; 
                        if(x!=y)fa[x]=x,siz[y]-=siz[x];
                        top--;
                    }
                }
        }
        for(int i=1;i<=p;i++) puts(ans[i]?"Yes":"No");
        return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    Windows 经典DOS命令大全
    Linux常用命令大全(全面)
    Nova 实现的 Fit Instance NUMA to Host NUMA 算法
    计算机组成原理 — 冯诺依曼体系结构
    计算机组成的基本硬件设备
    OpenStack 高性能虚拟机之大页内存
    Ironic 的 Rescue 救援模式实现流程
    SQLite 版本引发的 Python 程序调用问题
    Ceph 故障修复记录(持续更新)
    注册 Ironic 裸金属节点并部署裸金属实例
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6624776.html
Copyright © 2020-2023  润新知