zoukankan      html  css  js  c++  java
  • 【BZOJ】5028: 小Z的加油店

    【算法】数学+线段树/树状数组

    【题解】

    首先三个操作可以理解为更相减损术或者辗转相除法(待证明),所以就是求区间gcd。

    这题的问题在线段树维护gcd只能支持修改成一个数,不支持加一个数。

    套路:gcd(a,b,c,d,e)=gcd(a-b,b-c,c-d,d-e,e),也就是所有数的gcd可以转化为所有差值和最后一个数的gcd

    那么只需要查询区间差值gcd和一个数。

    对于区间差值gcd查询,区间加数只会导致两个单位的差值变化,所以可以用线段树单点修改区间查询gcd。

    对于一个数查询,就用树状数组维护区间加值和单点查询就行了。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    #define lowbit(x) x&(-x)
    using namespace std;
    const int maxn=100010;
    struct tree{int l,r,g;}t[maxn*4];
    int a[maxn],c[maxn],n,m;
    
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    int gcd(int a,int b){return !b?a:gcd(b,a%b);}
    void modify(int x,int k){for(int i=x;i<=n;i+=lowbit(i))c[i]+=k;}
    int query(int x){int as=0;for(int i=x;i>=1;i-=lowbit(i))as+=c[i];return as;}//as给初值 
    void build(int k,int l,int r){
        t[k].l=l;t[k].r=r;
        if(l==r)t[k].g=a[l]-a[l+1];
        else{
            int mid=(l+r)>>1;
            build(k<<1,l,mid);
            build(k<<1|1,mid+1,r);
            t[k].g=gcd(t[k<<1].g,t[k<<1|1].g);
        }
    }
    void add(int k,int x,int v){
        if(t[k].l==t[k].r)t[k].g+=v;
        else{
            int mid=(t[k].l+t[k].r)>>1;
            if(x<=mid)add(k<<1,x,v);
            else add(k<<1|1,x,v);
            t[k].g=gcd(t[k<<1].g,t[k<<1|1].g);
        }
    }
    int ask(int k,int l,int r){
        if(l<=t[k].l&&t[k].r<=r)return t[k].g;
        else{
            int mid=(t[k].l+t[k].r)>>1,x1=0,x2=0;
            if(l<=mid)x1=ask(k<<1,l,r);
            if(r>mid)x2=ask(k<<1|1,l,r);
            if(x1&&x2)return gcd(x1,x2);
            if(x1)return x1;
            return x2;
        }
    }    
    int ab(int x){return x>0?x:-x;}
    int main(){
        n=read();m=read();
        a[0]=0;
        for(int i=1;i<=n;i++)a[i]=read(),modify(i,a[i]-a[i-1]);
        a[n+1]=1;
        build(1,1,n);
        for(int i=1;i<=m;i++){
            int p=read(),l=read(),r=read();
            if(l>r)swap(l,r);
            if(p==1){
                if(l==r)printf("%d
    ",query(r));
                else printf("%d
    ",ab(gcd(ask(1,l,r-1),query(r))));//gcd不怕0
            }
            else{
                int v=read();
                if(l>1)add(1,l-1,-v);//注意边界! 
                add(1,r,v);
                modify(l,v);
                if(r<n)modify(r+1,-v);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    树形DP新识
    HDU3652 B-number 数位DP第二题
    HDU3555 Bomb 数位DP第一题
    数位DP新识
    Codeforces Round #371 & HihoCoder1529【玄学】
    hihocoder1618 单词接龙
    后缀数组 逐步探索
    HDU2157 How many ways矩阵再识
    阿里云安全中心:自动化安全闭环实现全方位默认安全防护
    趣谈预留实例券,一文搞懂云上省钱最新玩法
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7501859.html
Copyright © 2011-2022 走看看