小阳的贝壳(区间GCD)
题目链接:https://ac.nowcoder.com/acm/problem/26255
题目大意:区间修改,求区间(gcd),区间相邻两值之间差值最大值。
分析:
首先是区间相邻两值之间差值最大值,这个比较好求不作讨论
求区间(gcd)的,很显然首先想到线段树,维护序列,区间查询,可惜这里是区间修改,直接维护序列a显然是不行的。以为在区间里无法得知修改后的(gcd),并(gcd)也无法通过懒标记下放给子节点(反正我是不知道)
由某些数学公式可得: (gcd(a,b)=gcd (a,b-a)))(貌似是九章算术里面的吧),这样就能推出 (gcd(a,b,c)= gcd(a,b-a,c-b))
得到这个公式后,观察可发现我们把维护的序列a,更改为维护序列a的差分序列b,因为维护差分序列b,就实现了对线段树的单点修改,也就不必考虑修改后区间的(gcd)和(gcd)懒标记下放。
代码说明:
线段树维护区间(sum),区间(gcd),区间(max),其中(sum)的序列为(a,gcd)和(max)的序列为(b)
我们对(b)序列区间修改只需要(b[ql]+x,b[qr+1]-x)。
求解区间(gcd(ql,qr))为 (gcd(sum[ql],b[ql+1...qr]))。
注意:
注意绝对值问题。
代码:
#include <iostream>
#define lch 2*k
#define rch 2*k+1
#define mid (l+r)/2
using namespace std;
typedef long long ll;
const int N=1e5+7;
int n,m;
ll a[N],pre[N];
ll tree[4*N],god[4*N],maxn[4*N],tap[4*N]; // 区间sum,区间gcd,区间差值max,区间sum懒标记
ll gcd(ll x,ll y){
if(x==0&&y==0) return 0;
return y==0? x: gcd(y,x%y);
}
void init(int k,int l,int r){
tap[k]=0;
if(l>=r){
tree[k]=a[l];
god[k]=pre[l];
maxn[k]=pre[l];
return;
}
init(lch,l,mid);
init(rch,mid+1,r);
tree[k]=tree[lch]+tree[rch];
maxn[k]=max(abs(maxn[lch]),abs(maxn[rch]));
god[k]=gcd(abs(god[lch]),abs(god[rch]));
}
void pushdown(int k,int zlen,int ylen){
if(tap[k]){
tree[lch]+=tap[k]*zlen;
tree[rch]+=tap[k]*ylen;
tap[lch]+=tap[k];
tap[rch]+=tap[k];
tap[k]=0;
}
}
void updateT(int k,int l,int r,int ql,int qr,ll cost){
if(ql<=l&&r<=qr){
tree[k]+=cost*(r-l+1);
tap[k]+=cost;
return;
}
pushdown(k,mid-l+1,r-mid);
if(ql<=mid) updateT(lch,l,mid,ql,qr,cost);
if(mid+1<=qr) updateT(rch,mid+1,r,ql,qr,cost);
tree[k]=tree[lch]+tree[rch];
}
ll qryT(int k,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
return tree[k];
}
int sum1=0,sum2=0;
pushdown(k,mid-l+1,r-mid);
if(ql<=mid) sum1=qryT(lch,l,mid,ql,qr);
if(mid+1<=qr) sum2=qryT(rch,mid+1,r,ql,qr);
return sum1+sum2;
}
void updateG(int k,int l,int r,int ql,int qr,ll cost){
if(ql>n) return;
if(ql<=l&&r<=qr){
god[k]+=cost;
maxn[k]+=cost;
return ;
}
if(ql<=mid) updateG(lch,l,mid,ql,qr,cost);
if(mid+1<=qr) updateG(rch,mid+1,r,ql,qr,cost);
//注意取abs
god[k]=gcd(abs(god[lch]),abs(god[rch]));
maxn[k]=max(abs(maxn[lch]),abs(maxn[rch]));
}
ll qryG(int k,int l,int r,int ql,int qr){
if(ql>qr){
return 0;
}
if(ql<=l&&r<=qr){
return god[k];
}
int gcd1=0,gcd2=0;
if(ql<=mid) gcd1=qryG(lch,l,mid,ql,qr);
if(mid+1<=qr) gcd2=qryG(rch,mid+1,r,ql,qr);
//注意取abs
return gcd(abs(gcd1),abs(gcd2));
}
ll qryM(int k,int l,int r,int ql,int qr){
if(ql>qr) return 0;
if(ql<=l&&r<=qr){
return maxn[k];
}
int max1=0,max2=0;
if(ql<=mid) max1=qryM(lch,l,mid,ql,qr);
if(mid+1<=qr) max2=qryM(rch,mid+1,r,ql,qr);
//注意取abs
return max(abs(max1),abs(max2));
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=a[i]-a[i-1];
}
init(1,1,n);
while(m--){
int op,ql,qr,x;
scanf("%d %d %d",&op,&ql,&qr);
if(op==1){
scanf("%d",&x);
updateT(1,1,n,ql,qr,x); //序列a区间修改
updateG(1,1,n,ql,ql,x); //差分b区间修改
updateG(1,1,n,qr+1,qr+1,-x);
}else if(op==2){
printf("%lld
",qryM(1,1,n,ql+1,qr));
}else{
//由公式ql到qr的gcd为ql的值与ql到qr之间的差分的gcd的gcd
//注意为ql+1
printf("%lld
",gcd(qryT(1,1,n,ql,ql),qryG(1,1,n,ql+1,qr)));
}
}
}