zoukankan      html  css  js  c++  java
  • 区间gcd (带修) 线段树

    题目链接:https://ac.nowcoder.com/acm/contest/1033/B

    再次吐槽CH

    区间gcd再加区间修改。

    一般求gcd的时候辗转相除法。

    gcd(x,y)=gcd(x,y-x)

    那么可以把这个公式推到3个项。

    gcd(x,y,z)=gcd(x,y-x,z-y)

    可以看出来这是一个差分数列。

    原数列为a[],差分数列为b[]。

    这样就可以用线段树在b[]上单点修改,l加上d,r+1减去d。

    再用树状数组维护出一个c[]数组,区间修改,单点查询a[]数组。

    这样的话答案就是

    gcd(a[l]+aks_c(l),ask_t(1,1,n,l+1,r))

    就可以较小复杂度处理了。

    代码如下:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=500001;
    int n,m;
    ll c[maxn],a[maxn],b[maxn];
    struct node{
        ll ans;
        #define ans(x) t[x].ans
    }t[maxn<<2];
    inline ll gcd(ll x,ll y){
        return y ? gcd(y,x%y) : x;
    }
    inline void build(int p,int l,int r){
        if(l==r){
            ans(p)=b[l];return;
        }
        int mid=(l+r)>>1;
        build(p<<1,l,mid);build(p<<1|1,mid+1,r);
        ans(p)=gcd(ans(p<<1),ans(p<<1|1));
    }
    inline void change(int p,int l,int r,int x,ll d){
        if(l==r&&r==x){
            ans(p)+=d;return;
        }
        int mid=(l+r)>>1;
        if(x<=mid) change(p<<1,l,mid,x,d);
        else change(p<<1|1,mid+1,r,x,d);
        ans(p)=gcd(ans(p<<1),ans(p<<1|1));
    }
    inline ll askt(int p,int l,int r,int x,int y){
        if(x<=l&&r<=y)
            return abs(ans(p));
        int mid=(l+r)>>1;ll res=0;
        if(x<=mid) res=gcd(res,askt(p<<1,l,mid,x,y));
        if(y>mid) res=gcd(res,askt(p<<1|1,mid+1,r,x,y));
        return abs(res);
    }
    inline void add(int x,ll y){
        for(;x<=n;x+=(x&-x))
            c[x]+=y;
    }
    inline ll askc(int x){
        ll ans=0;
        for(;x;x-=(x&-x))
            ans+=c[x];
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            b[i]=a[i]-a[i-1];
        }
        build(1,1,n);
        for(int i=1;i<=m;i++){
            char ch[2];scanf("%s",ch);
            int l,r;scanf("%d%d",&l,&r);
            if(ch[0]=='Q'){
                printf("%lld
    ",(ll)gcd(a[l]+askc(l),askt(1,1,n,l+1,r)));
            }else{
                ll d;scanf("%lld",&d);
                change(1,1,n,l,d);
                if(r<n) change(1,1,n,r+1,-d);
                add(l,d);add(r+1,-d);
            }
        }
        return 0;
    }
  • 相关阅读:
    CentOS创建vsftp进行读写操作的简单方法
    [知乎]经典古诗词
    js仿手机端九宫格登录功能
    Android Studio 小技巧合集
    Android MVPR 架构模式
    剖析OkHttp缓存机制
    Java Observable 模式
    使用Rxjava缓存请求
    ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别
    ThreadPoolExecutor使用介绍
  • 原文地址:https://www.cnblogs.com/ChrisKKK/p/11489700.html
Copyright © 2011-2022 走看看