zoukankan      html  css  js  c++  java
  • CH 4302 Interval GCD 题解

    题意
    给定一个长度为N的数列A,以及M条指令 (N≤5* 10^5, M<=10^5),每条指令可能是以下两种之一:

    “C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

    “Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

    由《九章算术》中的更相减损我们知道gcd(x,y)=gcd(x,y-x)同理可以推到多个整数。(可以用数学归纳法证明)

    因此,构造一个长度为N的新数列B,其中B[i]=A[i]-A[i-1],B[1]为任意值,数列B称作数列A的差分序列。我们可以用线段树维护序列B的区间最大公约数。询问“Q l r”,就等于求出gcd(A[l],ask(1,l+1,r))。

    在指令“C l r d”下只有B[l]加d,B[r+1]减d,所以直接线段树两次单点修改即可,对于原序列A,我们之间用树状数组“区间修改,单点查询”维护即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=500010;
    struct node{
        int l,r;
        long long data;
    } t[maxn*4];
    long long a[maxn],b[maxn],c[maxn];
    int n,m,l,r;
    long long x;
    long long gcd(long long a,long long b) {
        return b ? gcd(b,a%b) : a;
    }
    void build(int p,int l,int r){
        t[p].l=l;t[p].r=r;
        if(l==r){t[p].data=b[l];return;}
        int mid=(l+r)/2;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        t[p].data=gcd(t[p*2].data,t[p*2+1].data);
    }
    void change(int p,int x,long long v){
        if(t[p].l==t[p].r){t[p].data+=v;return;}
        int mid=(t[p].l+t[p].r)/2;
        if(x<=mid) change(p*2,x,v);
        else change(p*2+1,x,v);
        t[p].data=gcd(t[p*2].data,t[p*2+1].data);
    }
    long long ask(int p,int l,int r){
        if(l<=t[p].l&&r>=t[p].r) return abs(t[p].data);
        int mid=(t[p].l+t[p].r)/2;
        long long val=0;
        if(l<=mid) val=gcd(val,ask(p*2,l,r));
        if(r>mid) val=gcd(val,ask(p*2+1,l,r));
        return abs(val);
    }
    int lowbit(int x){
        return x&-x;
    }
    long long sum(int x) {
        long long tmp=0;
        for(;x;x-=lowbit(x)) tmp+=c[x];
        return tmp;
    }
    void add(int x,long long y) {
        for(;x<=n;x+=lowbit(x)) c[x]+=y;
    }
    int main(){
        cin >>n>>m;
        for(int i=1;i<=n;++i){
            scanf("%lld",&a[i]);
            b[i]=a[i]-a[i-1];
        }
        build(1,1,n);
        while(m--){
            char str[2];
            scanf("%s",str);
            scanf("%d %d",&l,&r);
            if(str[0]=='Q'){
                long long tmp=a[l]+sum(l);
                long long val=l<r ? ask(1,l+1,r) : 0;
                printf("%lld
    ",gcd(tmp,val));
            }
            else{
                scanf("%lld",&x);
                change(1,l,x);
                if(r<n)change(1,r+1,-x);
                add(l,x);
                add(r+1,-x);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    c# 基础连接已经关闭: 连接被意外关闭,错误的解决
    关于SSIS中代码页(Code Page) 相关错误
    WinAPI: CopyFileEx
    RegularExpressions(5) RegularExpressions 成员(二) IRegex
    RegularExpressions(4) RegularExpressions 成员(一)
    RegularExpressions(3) RegularExpressions 的工作思路
    一句话复制整个文件夹(当然包括嵌套文件夹) 回复 "张哆哆" 的问题
    有趣的 TBitBtn.Kind
    如何用 GDI+ 高质量地缩放图片 回复 "程序牛" 的问题
    如何用 GDI 绘制阴影文字 回复 "Splendour" 的问题
  • 原文地址:https://www.cnblogs.com/donkey2603089141/p/11416376.html
Copyright © 2011-2022 走看看