题目描述
小阳手中一共有 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;
}
}