zoukankan      html  css  js  c++  java
  • BZOJ4537: [Hnoi2016]最小公倍数

    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。

    (注意:第一个字母大写,其余字母小写) 

    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

    题解Here!
    $AHOI2016$个人认为最毒瘤的一题。。。
     
    首先有个式子:$lcm(2^{a_1} imes 3^{b_1},2^{a_2} imes 3^{b_2})=2^{max(a_1,a_2)} imes 3^{max(b_1,b_2)}$
     
    于是就转化成了:
     
    求一条$u$到$v$的路径$a_1,a_2,a_3,...,a_k$使得$max_{i=1}^ka_i==A,max_{i=1}^kb_i==B$。
     
    我们发现只有$a<=A,b<=B$的边对答案有贡献。
     
    那么我们可以把所有满足条件的边加进去,然后用并查集判断两点是否联通并且联通块内最大值是否合法。
     
    而对于这种有两个限制的题目,一般的套路就是条件按照第一种权值为关键字排序,询问按照第二种关键字排序。

    然后给条件分块,对于一个块只把第一关键字符合条件的询问放进去。

    再把当前块前面的点按照第二关键字排序。

    这样当前块前面的点都是符合当前询问点对于第一关建字条件的,而且第二关键字都是单调的。

    然后对于每个询问,暴力处理一下当前块的贡献。

    所以我们可以先把边按$a$排序。

    对于一个块$[i,i+size]$,我们把询问的$A$在$[a[i],a[i+size])$之间的询问放进去。($size$是块的大小)

    然后把$[1,i)$的边按照$b$从小到大排序,边扫边加边,用并查集维护连通性和最大值。

    对于一个询问暴力把$[i,i+size)$内满足$a<=A,b<=B$的边加进去。

    注意,由于分块处理完一块后要“处理作案痕迹”,即撤销操作。

    所以我们使用可回溯并查集。

    不能使用压缩路径,否则难以实现撤销$uniun$操作,而只能使用并查集的启发式合并,即按秩合并。

    这样子的话效率似乎是均摊$O(log_2n)$。

    同时记录操作,然后撤销即可。

    然后那个块的大小$size$开$sqrt{mlog_2n}$应该是最优的,不过我为了省事就直接$sqrt m$了。。。
     
    恶心他妈给恶心开门——恶心到家了。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define MAXN 50010
    using namespace std;
    int n,m,q,l,sum;
    int fa[MAXN],size[MAXN],maxa[MAXN],maxb[MAXN];
    bool ans[MAXN];
    struct Question{
        int u,v,a,b,id;
    }a[MAXN<<1],b[MAXN],c[MAXN];
    struct Set{
        int u,v,fa,size,maxa,maxb;
    }h[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline bool cmpa(const Question &p,const Question &q){
        if(p.a==q.a)return p.b<q.b;
        return p.a<q.a;
    }
    inline bool cmpb(const Question &p,const Question &q){
        if(p.b==q.b)return p.a<q.a;
        return p.b<q.b;
    }
    int find(int x){return fa[x]==x?x:find(fa[x]);}
    void uniun(int u,int v,int a,int b){
        u=find(u);v=find(v);
        if(size[u]>size[v])swap(u,v);
        sum++;
        h[sum].u=u;h[sum].v=v;h[sum].maxa=maxa[v];h[sum].maxb=maxb[v];
        h[sum].fa=fa[u];h[sum].size=size[v];
        if(u==v){
            maxa[u]=max(maxa[u],a);
            maxb[u]=max(maxb[u],b);
            return;
        }
        fa[u]=v;
        size[v]+=size[u];
        maxa[v]=max(maxa[v],max(maxa[u],a));
        maxb[v]=max(maxb[v],max(maxb[u],b));
    }
    void back(){
        while(sum){
            fa[h[sum].u]=h[sum].fa;
            size[h[sum].v]=h[sum].size;
            maxa[h[sum].v]=h[sum].maxa;
            maxb[h[sum].v]=h[sum].maxb;
            sum--;
        }
    }
    void work(){
        for(int i=1;i<=m;i+=l){
            int top=0;
            for(int j=1;j<=q;j++)if(b[j].a>=a[i].a&&(i+l>m||b[j].a<a[i+l].a))c[++top]=b[j];
            sort(a+1,a+i,cmpb);
            for(int j=1;j<=n;j++){
                fa[j]=j;
                size[j]=1;
                maxa[j]=maxb[j]=-1;
            }
            for(int j=1,k=1;j<=top;j++){
                for(;k<i&&a[k].b<=c[j].b;k++)uniun(a[k].u,a[k].v,a[k].a,a[k].b);
                sum=0;
                for(int p=i;p<i+l&&p<=m;p++)if(a[p].a<=c[j].a&&a[p].b<=c[j].b)uniun(a[p].u,a[p].v,a[p].a,a[p].b);
                int x=find(c[j].u),y=find(c[j].v);
                ans[c[j].id]=(x==y&&maxa[x]==c[j].a&&maxb[x]==c[j].b);
                back();
            }
        }
        for(int i=1;i<=q;i++){
            if(ans[i])printf("Yes
    ");
            else printf("No
    ");
        }
    }
    void init(){
        n=read();m=read();
        for(int i=1;i<=m;i++){
            a[i].u=read();a[i].v=read();a[i].a=read();a[i].b=read();
            a[i].id=i;
        }
        q=read();
        for(int i=1;i<=q;i++){
            b[i].u=read();b[i].v=read();b[i].a=read();b[i].b=read();
            b[i].id=i;
        }
        sort(a+1,a+m+1,cmpa);
        sort(b+1,b+q+1,cmpb);
        l=sqrt(m);
    }
    int main(){
        init();
        work();
    	return 0;
    }
    
     
  • 相关阅读:
    gcc/g++动态链接库和静态库的链接顺序
    linux文件描述符--转载
    mysql之test表
    linux之eventfd()
    spring data jpa实现多条件查询(分页和不分页)
    List的remove方法里的坑
    启动Spring boot报错:nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
    CentOS下yum安装jdk
    CentOS下yum安装mysql
    Redis有序Set、无序Set的使用经历
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9459217.html
Copyright © 2011-2022 走看看