5028: 小Z的加油店(线段树)

  NOI2012魔幻棋盘弱化版

  gcd(a,b,c,d,e)=gcd(a,b-a,c-b,d-c,e-d)

  然后就可以把区间修改变成差分后的点修了。

  用BIT维护原序列,线段树维护区间gcd,支持点修区查

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=500010;
struct poi{int sum;}tree[maxn<<2];
int n,m,ty,x,y,z,ans;
int a[maxn],bit[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int lowbit(int x){return x&-x;}
inline void add(int x,int delta){for(;x<=n;x+=lowbit(x))bit[x]+=delta;}
inline int querybit(int x){int sum=0;for(;x;x-=lowbit(x))sum+=bit[x];return sum;}
inline void pushup(int x){tree[x].sum=gcd(tree[x<<1].sum,tree[x<<1|1].sum);}
void build(int x,int l,int r)
{
    if(l==r){tree[x].sum=a[l]-a[l-1];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    pushup(x);
}
void update(int x,int l,int r,int cx,int delta)
{
    if(l==r){tree[x].sum+=delta;return;}
    int mid=(l+r)>>1;
    if(cx<=mid)update(x<<1,l,mid,cx,delta);
    else update(x<<1|1,mid+1,r,cx,delta);
    pushup(x);
}
void query(int x,int l,int r,int cl,int cr)
{
    if(cl<=l&&r<=cr){ans=gcd(ans,abs(tree[x].sum));return;}
    int mid=(l+r)>>1;
    if(cl<=mid)query(x<<1,l,mid,cl,cr);
    if(cr>mid)query(x<<1|1,mid+1,r,cl,cr);
}
int main()
{
    read(n);read(m);
    for(int i=1;i<=n;i++)read(a[i]),add(i,a[i]),add(i+1,-a[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        read(ty);read(x);read(y);
        if(ty==1)ans=querybit(x),query(1,1,n,x+1,y),printf("%d
",ans);
        else
        {
            read(z);add(x,z);add(y+1,-z);
            update(1,1,n,x,z);update(1,1,n,y+1,-z);
        }
    }
}
View Code