• Luogu T9376 区间GCD


    题目背景

    题目描述

    给定一长度为n的动态序列,请编写一种数据结构,要求支持m次操作,包括查询序列中一闭区间中所有数的GCD,与对一闭区间中所有数加上或减去一个值。

    输入输出格式

    输入格式:

    第1行两个数n,m,表示序列长度和操作次数。

    第2行n个数ai,表示给定序列。

    第3行至第m+2行,每行3~4个数:

    (1) 1 x y k 表示将[x,y]上的所有数加上k。

    (2) 2 x y 表示询问[x,y]上所有数的GCD。

    输出格式:

    对所有操作2,输出一个数,表示询问结果。

    输入输出样例

    输入样例#1:
    7 3
    4 8 2 6 5 7 10
    2 1 4
    1 2 3 7
    2 2 3
    
    输出样例#1:
    2
    3
    

    说明

    定义:a,b∈Z时,gcd(a,b)=gcd(abs(a),abs(b))

    对于30%的数据,n,m<=1000。

    对于90%的数据,n,m<=100000。

    对于100%的数据,n,m<=200000,ai<=1e7(初始),abs(k)<=1e7。

    题解:

    如果题目要求改为只支持区间查询,那么线段树或ST表都可以很方便地实现。进一步思考,区间修改无法用普通线段树实现的根本原因在于对[l,r]修改后[l,r]的结果无法O(1)计算出来。
    如果区间修改改为单点修改,则可以用线段树暴力log(n)修改。

    此处证明一个引理:gcd(a1,a2,a3,...,ai)=gcd(a1,a2-a1,a3-a2,...ai-ai-1).
    设S为ai的公因数集合,T为ai-ai-1的公因数集合
    设p为ai的任意一个公因数,则有p|ai,由整除的性质知p|ai-ai-1,则p一定是ai-ai-1的公因数,所以S是T的子集。
    同理,设q为ai-ai-1的任意一个公因数,运用同样的性质可知q一定是ai的公因数,所以T是S的子集。
    综上,S=T,所以max{S}=max{T},即gcd(a1,a2,a3,...,ai)=gcd(a1,a2-a1,a3-a2,...ai-ai-1).

    所以我们将原数组a进行差分,设差分后数组为d,区间查询[l,r]则转化为gcd(gcd(d[l+1,r]),a[l]);差分后区间修改变为单点修改,可用线段树暴力实现。

    具体操作:将原数组进行差分,用一棵支持单点修改的线段树维护gcd,将差分数组用一个树状数组维护前缀和(用来求出变化后的a[l],也可以合并在线段树中)。
    注意:差分时对区间[l,r]涉及到对r+1的操作,为防止溢出,线段树区间增大至[1,n+1]。

    代码如下:

    #include<bits/stdc++.h>
    #define LL long long
    #define lowbit(x) x&(-x)
    using namespace std;
    const int maxn=2e5+10;
    LL node[4*maxn],a[maxn],c[maxn],d[maxn];
    int n,m;LL ans;
    LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
    void pushup(int x){node[x]=abs(gcd(node[x<<1],node[x<<1|1]));}
    void build(int x,int l,int r) {
        if(l==r){node[x]=d[l];return;}
        int mid=(l+r)>>1;
        build(x<<1,l,mid);build(x<<1|1,mid+1,r);
        pushup(x);
    }
    void change(int x,int l,int r,int pos,int d) {
        if(l==r){node[x]+=d;return;}
        int mid=(l+r)>>1;
        if(pos<=mid){change(x<<1,l,mid,pos,d);}
        else{change(x<<1|1,mid+1,r,pos,d);}
        pushup(x);
    }
    void query(int x,int l,int r,int sj,int tj) {
        if(sj<=l&&r<=tj){ans=abs(gcd(node[x],ans));return;}
        int mid=(l+r)>>1;
        if(sj<=mid){query(x<<1,l,mid,sj,tj);}
        if(mid+1<=tj){query(x<<1|1,mid+1,r,sj,tj);}
        pushup(x);
    }
    void add(int x,int d) {
        int i;
        for(i=x;i<=n;i+=lowbit(i)){c[i]+=d;}
    }
    LL sum(int x) {
        int i;LL ans=0;
        for(i=x;i>=1;i-=lowbit(i)){ans+=c[i];}
        return ans;
    }
    int main() {
        int i,j,flag,l,r,dlt;
        cin>>n>>m;
        for(i=1;i<=n;i++){scanf("%lld",&a[i]);}
        n++;
        for(i=1;i<=n;i++){d[i]=a[i]-a[i-1];add(i,d[i]);}
        build(1,1,n);
        //for(i=1;i<=3*n;i++){printf("i=%d node[i]=%d ",i,node[i]);}
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&flag,&l,&r);
            if(flag==1){scanf("%d",&dlt);change(1,1,n,l,dlt);change(1,1,n,r+1,-dlt);add(l,dlt);add(r+1,-dlt);}
            else{ans=0;query(1,1,n,l+1,r);/*printf("ans=%d sum(l)=%d ",ans,sum(l));*/printf("%lld ",abs(gcd(ans,sum(l))));}
        }
        return 0;
    }

  • 相关阅读:
    创建Graphics对象与Pen对象
    GDI+图形图像处理技术——GDIPlus绘图基础
    WPF的组成架构
    文件监控只FileSystemWatcher控件
    文件夹选择之FolderBrowserDialog控件
    SaveFileDialog控件
    文件选择之OpenFileDialog控件
    编码与解码
    写一个翻译小工具
    【转】字符集与字符编码简介
  • 原文地址:https://www.cnblogs.com/XSC637/p/7423923.html
Copyright © 2020-2023  润新知