小阳的贝壳(线段树&差分数组)
思路:利用线段树和差分数组维护,对于区间修改,用差分数组即可实现,然后对于求区间内差分数组的最大值,用一个 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,b−a)
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+2…ar)=gcd(al,gcd(al,al+1),gcd(al+1,al+2)…,gcd(ar−1,ar))=gcd(al,al+1−al,al+2−al+1,…ar−ar−1)
即可求出区间最大公因数。
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;
}