zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x43线段树 区间最大公约数

    题目链接:https://www.acwing.com/problem/content/247/

    操作有两种:求区间最大公约数+区间修改。

    两者的结合可以运用更相损减术,维护一个差分数列,用一个树状数组维护数列的差分前缀和,其中要注意的是,树状数组和线段树的维护中要防止索引的越界!!其次,gcd得到的数可能是负数,所以在进入最小性的区间中之后需要对gcd取绝对值,gcd(a,-b)=gcd(a,b)。不能直接对差分之后的区间的gcd取绝对值,因为gcd(a+1,b)!=gcd(-a+1,b)。要在答案返回的时候进行更新。

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int maxn = 500010;
    ll a[maxn],b[maxn],c[maxn];
    int n,m;
    ll gcd(ll x,ll y){
        return y?gcd(y,x%y):x;
    }
    struct node{
        int l,r;
        ll gcd;
    }t[maxn<<2]; 
    ll sum(int x){//差分数列的前缀和 
        ll ans=0;
        while(x){
            ans+=c[x];
            x-=x&(-x);
        }
        return ans;
    }
    ll add(int x,ll C){//树状数组单点更新 
        while(x<=n){
            c[x]+=C;
            x+=x&(-x);
        }
    }
    void build(int rt,int l,int r){
        t[rt].l=l;
        t[rt].r=r;
        if(l==r){
            t[rt].gcd=b[l];
            return ;
        }
        int mid=(t[rt].l+t[rt].r)>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        t[rt].gcd=gcd(t[rt<<1].gcd,t[rt<<1|1].gcd);
    }
    void update(int rt,int pos,ll C){
        if(t[rt].l==t[rt].r){
            t[rt].gcd+=C;
            return ;
        }
        int mid=(t[rt].l+t[rt].r)>>1;
        if(pos<=mid)update(rt<<1,pos,C);
        else update(rt<<1|1,pos,C);
        t[rt].gcd=gcd(t[rt<<1].gcd,t[rt<<1|1].gcd);
    }
    ll query(int rt,int L,int R){
        if(t[rt].l>=L && t[rt].r<=R){
            return abs(t[rt].gcd);
        }
        int mid=(t[rt].l+t[rt].r)>>1;
        ll ans=0;
        if(L<=mid)ans=gcd(ans,query(rt<<1,L,R));
        if(R>mid)ans=gcd(ans,query(rt<<1|1,L,R));
        return ans;
    }
    int main(){
        cin>>n>>m;
        b[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            b[i]=a[i]-a[i-1];
        }
        build(1,1,n);
        char s[10];
        int l,r;
        ll d;
        while(m--){
            scanf("%s",s);
            if(s[0]=='C'){
                scanf("%d%d%lld",&l,&r,&d);
                update(1,l,d);//维护两个差分数列 
                add(l,d);
                if(r+1<=n){//注意防止树状数组的下标越界 
                    update(1,r+1,-d);
                    add(r+1,-d);    
                }
            }else{
                scanf("%d%d",&l,&r);
                ll ans=gcd(a[l]+sum(l),query(1,l+1,r));
                printf("%lld
    ",ans);
            }
        }
    }
  • 相关阅读:
    解决ubuntu中zip解压的中文乱码问题
    GCC 静态库和动态库
    交互式shell和非交互式shell、登录shell和非登录shell的区别
    牛顿迭代法实现平方根函数sqrt
    Sqrt函数高效实现
    Http、tcp、Socket连接区别
    C++11的一般概念——The C++ standard library, 2nd Edition 笔记(二)
    C++11新特性——The C++ standard library, 2nd Edition 笔记(一)
    【Java线程与内存分析工具】VisualVM与MAT简明教程
    Java设计模式:Proxy(代理)模式
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13297616.html
Copyright © 2011-2022 走看看