数学+线段树 小阳的贝壳(牛客小白月赛16 H题)

小阳的贝壳

题目大意:
给你一个长度为n的数组,有初值,三种操作;

  1. 给区间[l,r]所有数加上x;
  2. 询问区间[l,r]相邻的元素差值最大值(取绝对值);
  3. 询问区间[l,r]所有数的最大公约数;

最难的就是第三问了,运用到了gcd的一些原理,如果不是很懂得话想不到;

gcd(a,b,c)=gcd(a,b-a,c-b)=gcd(a,gcd(b-a,c-b));

依此类推,不就是维护一个差分数组,如果求[l,r]的最大公约数,不就是求

gcd(a[l],gcd(b[l+1]…b[r]));(a[]代表原数组,b[]代表差分数组)

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100100;
const int M=2000000;
const LL mod=1e9+7;
int n,m,a[N];
struct Node{
	int l,r,sum,mx,mg;
}tr[N*4];
void pp(int k){
	tr[k].mx=max(tr[ls].mx,tr[rs].mx);
	tr[k].mg=__gcd(tr[ls].mg,tr[rs].mg);
	tr[k].sum=tr[ls].sum+tr[rs].sum;
}
void build(int l,int r,int k){
	tr[k].l=l,tr[k].r=r;
	if(l==r){
		tr[k].mx=tr[k].mg=abs(a[l]);
		tr[k].sum=a[l];
		return;
	}
	int d=(l+r)>>1;
	build(l,d,ls);build(d+1,r,rs);
	pp(k);
}
void update(int pos,int w,int k){
	if(tr[k].l==tr[k].r){
		a[tr[k].l]+=w;
		tr[k].sum=a[tr[k].l];
		tr[k].mx=tr[k].mg=abs(a[tr[k].l]);
		return;
	}
	int d=(tr[k].l+tr[k].r)>>1;
	if(pos<=d) update(pos,w,ls);
	else update(pos,w,rs);
	pp(k);
}
int query_max(int l,int r,int k){
	if(l>r) return 0;
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k].mx;
	int mx=0;
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) mx=max(query_max(l,r,ls),mx);
	if(r>d) mx=max(query_max(l,r,rs),mx);
	return mx;
}
int query_sum(int l,int r,int k){
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k].sum;
	int sum=0;
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) sum+=query_sum(l,r,ls);
	if(r>d) sum+=query_sum(l,r,rs);
	return sum;
}
int query_gcd(int l,int r,int k){
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k].mg;
	int mg=0;
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) mg=__gcd(mg,query_gcd(l,r,ls));
	if(r>d) mg=__gcd(mg,query_gcd(l,r,rs));
	return mg;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=n;i>=1;i--) a[i]-=a[i-1];//差分数组 
	build(1,n,1);
	for(int i=1;i<=m;i++){
		int opt;scanf("%d",&opt);
		if(opt==1){
			int l,r,x;scanf("%d%d%d",&l,&r,&x);
			update(l,x,1);//单点更新 
			if(r<n) update(r+1,-x,1);//单点更新 
		}
		else if(opt==2){
			int l,r;scanf("%d%d",&l,&r);
			printf("%d\n",query_max(l+1,r,1));
		}
		else{
			int l,r;scanf("%d%d",&l,&r);
			printf("%d\n",__gcd(query_sum(1,l,1),query_gcd(l+1,r,1)));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值