小阳的贝壳(区间GCD)

小阳的贝壳(区间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)));
		}	
	}
}
原文地址:https://www.cnblogs.com/kksk/p/14276500.html