牛客小白赛16:H-小阳的贝壳 (线段树维护带区间修改的gcd)

4 篇文章 0 订阅
0 篇文章 0 订阅

题目链接:https://ac.nowcoder.com/acm/contest/949/H
题目大意:
在这里插入图片描述
题解:模板题,用线段树维护区间gcd很容易维护,维护区间带修改的gcd需要用到gcd的性质(看似简单但我不会)。设有区间有 a , b , c , d a,b,c,d a,b,c,d 数字,则 g c d ( a , b , c , d ) = g c d ( a , b − a , c − b , d − c ) gcd(a,b,c,d) = gcd(a,b - a,c - b, d - c) gcd(a,b,c,d)=gcd(a,ba,cb,dc),容易发现除了第一个数字,后面都是差分的形式(知识盲区)。再有区间 g c d ( a , b , c , d ) = g c d ( a , g c d ( b , c , d ) ) gcd(a,b,c,d) = gcd(a,gcd(b,c,d)) gcd(a,b,c,d)=gcd(a,gcd(b,c,d)), 即gcd具有区间可加性,可以用线段树维护合并gcd,因此这题用线段树维护 a a a数组的差分数组的区间gcd即可,区间修改对于差分数组而言也是端点修改,所有的修改都是单点修改。注意查询的时候左端点不是差分形式,要求前缀和,可以用线段树也维护区间和快速求前缀和。

#include<bits/stdc++.h>
using namespace std;
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
const int maxn = 1e5 + 10;
int n,m,a[maxn];
int g[maxn << 2],val[maxn << 2],sum[maxn << 2];
int gcd(int a,int b) {
	return !b ? a : gcd(b,a % b);
}
void update(int p,int v,int rt,int l,int r) {
	if(l == r) {
		sum[rt] = v;
		val[rt] = max(v,-v);
		g[rt] = val[rt];
		return;
	}
	int mid = l + r >> 1;
	if(p > mid) update(p,v,rson);
	else update(p,v,lson);
	g[rt] = gcd(g[rt << 1],g[rt << 1 | 1]);
	val[rt] = max(val[rt << 1],val[rt << 1 | 1]);
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int query(int L,int R,int type,int rt,int l,int r) {
	if(L <= l && r <= R) {
		if(type == 1) return val[rt];
		else if(type == 2) return g[rt];
		else if(type == 3) return sum[rt];
	}
	int mid = l + r >> 1;
	int mx = 0;
	if(type == 1) {
		if(mid >= L) mx = max(mx,query(L,R,type,lson));
		if(mid + 1 <= R) mx = max(mx,query(L,R,type,rson));
	}
	else if(type == 2) {
		if(mid >= L) mx = gcd(mx,query(L,R,type,lson));
		if(mid + 1 <= R) mx = gcd(mx,query(L,R,type,rson));		
	}
	else if(type == 3) {
		if(mid >= L) mx += query(L,R,type,lson);
		if(mid + 1 <= R) mx += query(L,R,type,rson);
	}
	return mx;
}
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];
		update(i,a[i],1,1,n);
	}
	
	for(int i = 1; i <= m; i++) {
		int type,x,y,v;
		scanf("%d%d%d",&type,&x,&y);
		if(type == 1) {
			scanf("%d",&v);
			a[x] += v;
			a[y + 1] -= v;
			update(x,a[x],1,1,n);
			if(y + 1 <= n)
				update(y + 1,a[y + 1],1,1,n);
		}
		else if(type == 2) {
			if(x == y) printf("0\n");
			else
				printf("%d\n",query(x + 1,y,1,1,1,n));
		}
		else {
			int p = query(1,x,3,1,1,n);
			if(x < y) p = gcd(p,query(x + 1,y,2,1,1,n));
			printf("%d\n",p);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值