小阳的贝壳(线段树&差分数组)

传送门

思路:利用线段树和差分数组维护,对于区间修改,用差分数组即可实现,然后对于求区间内差分数组的最大值,用一个 m x mx mx维护即可,然后对于询问区间的最大公因数,利用辗转相除法的结论: g c d ( a , b ) = g c d ( a , b − a ) gcd(a,b)=gcd(a,b-a) gcd(a,b)=gcd(a,ba)

g c d ( a l , a l + 1 , a l + 2 … a r ) = g c d ( a l , g c d ( a l , a l + 1 ) , g c d ( a l + 1 , a l + 2 ) … , g c d ( a r − 1 , a r ) ) = g c d ( a l , a l + 1 − a l , a l + 2 − a l + 1 , … a r − a r − 1 ) gcd(a_l,a_{l+1},a_{l+2}\dots a_r)\\ =gcd(a_l,gcd(a_{l},a_{l+1}),gcd(a_{l+1},a_{l+2})\dots,gcd(a_{r-1},a_r)) \\=gcd(a_l,a_{l+1}-a_l,a_{l+2}-a_{l+1},\dots a_r-a_{r-1}) gcd(al,al+1,al+2ar)=gcd(al,gcd(al,al+1),gcd(al+1,al+2),gcd(ar1,ar))=gcd(al,al+1al,al+2al+1,arar1)

即可求出区间最大公因数。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define mst(a) memset(a,0,sizeof a)
int n,m,col[N],d[N];
struct tree{
    int l,r,s,g,mx;
}a[N*4];
void re(int x){ //更新 
    a[x].s=a[x<<1].s+a[x<<1|1].s;
    a[x].g=__gcd(a[x<<1].g,a[x<<1|1].g);
    a[x].mx=max(a[x<<1].mx,a[x<<1|1].mx);
}
void build(int x,int l,int r){ //建树 
    a[x].l=l,a[x].r=r;
    if(l==r){
        a[x].g=a[x].mx=abs(d[l]);
        a[x].s=d[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    re(x);
}
ll query_sum(int x,int l,int r){  //利用差分数组求和 
     if(a[x].l>=l&&a[x].r<=r) return a[x].s;
    int mid=(a[x].l+a[x].r)>>1;
     ll ans=0;
      if(l<=mid) ans+=query_sum(x<<1,l,r);
     if(r>=mid+1) ans+=query_sum(x<<1|1,l,r);
    return ans;
}
ll query_gcd(int x,int l,int r){//区间gcd 
     if(a[x].l>=l&&a[x].r<=r) return a[x].g;
    int mid=(a[x].l+a[x].r)>>1;
     ll ans=0;
     if(l<=mid) ans=__gcd(ans,query_gcd(x<<1,l,r));
     if(r>=mid+1) ans=max(ans,query_gcd(x<<1|1,l,r));
    return ans;
}
ll query_max(int x,int l,int r){  //查询区间最大差分值. 
     if(a[x].l>=l&&a[x].r<=r) return a[x].mx;
     int mid=(a[x].l+a[x].r)>>1;
    ll ans=0;
     if(l<=mid) ans=max(ans,query_max(x<<1,l,r));
     if(r>=mid+1) ans=max(ans,query_max(x<<1|1,l,r));
    return ans;
}
void update(int x,int pos,int val){ //通过对差分数组操作实现区间加减. 
     if(a[x].l==pos&&a[x].r==pos){
         a[x].s+=val;
         a[x].mx=abs(a[x].s);
         a[x].g=abs(a[x].s);
         return;
     }
     int mid=(a[x].l+a[x].r)>>1;
     if(pos<=mid) update(x<<1,pos,val);
    if(pos>=mid+1) update(x<<1|1,pos,val);
    re(x);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&col[i]);
    for(int i=1;i<=n;i++) d[i]=col[i]-col[i-1];
    build(1,1,n);
    while(m--){
        int op,l,r,x;
        scanf("%d%d%d",&op,&l,&r);
        if(op==1){
        	scanf("%d",&x),update(1,l,x);
			if(r<n) update(1,r+1,-x); //r=n时,不需要再减一遍了. 
		}
        else if(op==2) printf("%lld\n",query_max(1,l+1,r));
        else printf("%lld\n",__gcd(query_sum(1,1,l),query_gcd(1,l+1,r)));///定理 
    }
	return 0;
}