#牛客网 小阳的贝壳 (线段树 + 差分数组)

 

题目描述

小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 colicoli 。现在小阳有 3 种操作:

1 l r x:给 [l,r][l,r] 区间里所有贝壳的颜色值加上 xx 。

2 l r:询问 [l,r][l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=rl=r 输出 0)。

3 l r :询问 [l,r][l,r] 区间里所有贝壳颜色值的最大公约数。

输入描述:

第一行输入两个正整数 n,mn,m,分别表示贝壳个数和操作个数。
第二行输入 nn 个数 colicoli,表示每个贝壳的初始颜色。
第三到第 m+2m+2 行,每行第一个数为 optopt,表示操作编号。接下来的输入的变量与操作编号对应。

输出描述:

共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。

示例1

输入

 

5 6
2 2 3 3 3
1 2 3 3
2 2 4
3 3 5
1 1 4 2
3 2 3
2 3 5

输出

 

3
3
1
3

备注:

1≤n,m≤105,1≤coli,x≤103,1≤opt≤3,1≤l≤r≤n1≤n,m≤105,1≤coli,x≤103,1≤opt≤3,1≤l≤r≤n

思路 : 看到操作一显然要用线段树,操作二用差分数组来维护,操作三也是差分数组,这里需要知道一个性质,gcd(a, b) = gcd(a, b - a) (如果b比a小加绝对值), 而他的拓展就是gcd(a, b, c, d, ..., ...) = gcd(a, b - a, c - b,  d - c,.. , ... ) ,所以要求一个区间内的最大公约数,只要求第一个值和后面每个相邻的差值的最大公约数就可以了

AC代码 :

#include<bits/stdc++.h>
#define ls rt << 1
#define rs ls | 1
using namespace std;
const int MAXN = 1e5 + 5;

int sum[MAXN * 4], max_[MAXN * 4];  // sum是求和数组, max_是最大值
int gc[MAXN * 4], p[MAXN];   // gc是最大公约数, p是差分数组
int n, m;
int gcd(int a, int b) {
	return !b ? a : gcd(b, a % b);
}
void push_up(int rt) {  操作
	sum[rt] = sum[ls] + sum[rs];
	max_[rt] = max(max_[ls], max_[rs]);
	gc[rt] = gcd(gc[ls], gc[rs]);
}
void build(int rt, int l, int r) {  // 建树
	if (l == r) {
		sum[rt] = p[l];
		max_[rt] = gc[rt] = abs(p[l]);  // 注意要加绝对值
		return;
	}
	int mid = (l + r) >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	push_up(rt);
}
void update(int rt, int X, int l, int r, int w) {  // 更新操作
	if (l == r) {
		p[l] += w;  
		sum[rt] = p[l];
		max_[rt] = gc[rt] = abs(p[l]);  // 加绝对值
		return;
	}
	int mid = (l + r) >> 1;
	if (mid >= X) update(ls, X, l, mid, w);
	else update(rs, X, mid + 1, r, w);
	push_up(rt);
}
int sum_query(int rt, int L, int R, int l, int r) {  // 区间求和
	if (l >= L && r <= R) return sum[rt];
	int mid = (l + r) >> 1, res = 0;
	if (mid >= L) res += sum_query(ls, L, R, l, mid);
	if (mid < R) res += sum_query(rs, L, R, mid + 1, r);
	return res;
}
int max_query(int rt, int L, int R, int l, int r) {  // 区间最大值
	if (l >= L && r <= R) return max_[rt];
	int mid = (l + r) >> 1, res = 0;
	if (mid >= L) res = max(res, max_query(ls, L, R, l, mid));
	if (mid < R) res = max(res, max_query(rs, L, R, mid + 1, r));
	return res;
}
int gcd_query(int rt, int L, int R, int l, int r) {  // 区间最大公约数
	if (l >= L && r <= R) return gc[rt];
	int mid = (l + r) >> 1, res = 0;
	if (mid >= L) res = gcd(res, gcd_query(ls, L, R, l, mid));
	if (mid < R) res = gcd(res, gcd_query(rs, L, R, mid + 1, r));
	return res;
}

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
	for (int i = n; i >= 1; i--) p[i] -= p[i - 1];   // 差分数组
	build(1, 1, n); 
	while (m--) {
		int L, R, W, op;
		scanf("%d%d%d", &op, &L, &R);
		if (op == 1) {
			scanf("%d", &W);  
			update(1, L, 1, n, W);  更新L
			if (R < n) update(1, R + 1, 1, n, -W);  // 更新R + 1
		}
		else if (op == 2) {
			if (L == R) { cout << 0 << endl; continue; }
			cout << max_query(1, L + 1, R, 1, n) << endl;
		}
		else
			cout << gcd(sum_query(1, 1, L, 1, n), gcd_query(1, L + 1, R, 1, n)) << endl;
	}
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值