bzoj5028小Z的加油店(线段树+差分)

题意:维护支持以下两种操作的序列:1 l r询问a[l...r]的gcd,2 l r x把a[l...r]全部+x

题解:一道经典题。根据gcd(a,b)=gcd(a-b,b)以及区间加可知,这题可以差分求解。然后只需要维护支持单点加减和区间查询gcd、差分和的线段树即可,复杂度O(nlog2n),因为要求区间gcd。

#include<bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int N=1e5+7;
int n,m,a[N],d[N<<2],s[N<<2];
void pushup(int rt){d[rt]=__gcd(d[rt<<1],d[rt<<1|1]),s[rt]=s[rt<<1]+s[rt<<1|1];}
void build(int l,int r,int rt)
{
    if(l==r){d[rt]=s[rt]=a[l];return;}
    int mid=l+r>>1;
    build(lson),build(rson);
    pushup(rt);
}
void update(int k,int v,int l,int r,int rt)
{
    if(l==r){a[l]+=v,d[rt]=s[rt]=a[l];return;}
    int mid=l+r>>1;
    if(k<=mid)update(k,v,lson);else update(k,v,rson);
    pushup(rt);
}
int query_d(int L,int R,int l,int r,int rt)
{
    if(L>R)return 0;
    if(L<=l&&r<=R)return d[rt];
    int mid=l+r>>1,ret=0;
    if(L<=mid)ret=__gcd(ret,query_d(L,R,lson));
    if(R>mid)ret=__gcd(ret,query_d(L,R,rson));
    return ret;
}
int query_s(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)return s[rt];
    int mid=l+r>>1,ret=0;
    if(L<=mid)ret+=query_s(L,R,lson);
    if(R>mid)ret+=query_s(L,R,rson);
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i;i--)a[i]-=a[i-1];
    build(1,n,1);
    while(m--)
    {
        int op,l,r,x;scanf("%d%d%d",&op,&l,&r);
        if(op==1)printf("%d\n",abs(__gcd(query_d(l+1,r,1,n,1),query_s(1,l,1,n,1))));
        else{
            scanf("%d",&x);
            update(l,x,1,n,1);
            if(r<n)update(r+1,-x,1,n,1);
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/hfctf0210/p/10711786.html