题意:给出一个数组 a a a, n n n个数, m m m次询问。有 3 3 3种询问, 1 , l , r , x ; 2 , l , r ; 3 , l , r 1,l,r,x;\quad2,l,r;\quad3,l,r 1,l,r,x;2,l,r;3,l,r。
1 , l , r , x 1,l,r,x 1,l,r,x:对区间 [ l , r ] [l,r] [l,r]中的每个数加上 x x x。
2 , l , r 2,l,r 2,l,r:询问 [ l , r ] [l,r] [l,r]区间里所有相邻数字的差(取绝对值) 的最大值。
3 , l , r 3,l,r 3,l,r:询问 [ l , r ] [l,r] [l,r]区间的所有数的最大公约数。
思路:用线段树维护数组 a a a的差分数组 c ( c [ i ] = a [ i ] − a [ i − 1 ] ) c(c[i] = a[i]-a[i-1]) c(c[i]=a[i]−a[i−1])。包括区间最大值,区间 g c d ( 最 大 公 约 数 ) gcd(最大公约数) gcd(最大公约数),和区间和。
结论: g c d ( a 1 , a 2 , a 3 ) = g c d ( a 1 , a 2 , a 3 ) gcd(a_1,a_2,a_3) = gcd(a_1,a_2,a_3) gcd(a1,a2,a3)=gcd(a1,a2,a3) 推 广 到 a n 有 g c d ( a 1 , a 2 . . . , a n ) = g c d ( a 1 , a 2 − a 1... , a n − a n − 1 ) 推广到a_n有gcd(a_1,a_2...,a_n) = gcd(a_1,a_2-a1...,a_n-a_{n-1}) 推广到an有gcd(a1,a2...,an)=gcd(a1,a2−a1...,an−an−1) g c d ( a 1 , a 2... , a n ) = g c d ( g c d ( a 1 , a 2.. a n 2 ) , g c d ( a n 2 + 1 , a n 2 + 2 . . . , a n ) ) gcd(a1,a2...,an)=gcd(gcd(a1,a2..a_{\frac{n}{2}}),gcd(a_{\frac{n}{2}+1},a_{\frac{n}{2}+2}...,a_n)) gcd(a1,a2...,an)=gcd(gcd(a1,a2..a2n),gcd(a2n+1,a2n+2...,an))所以可以直接用线段树维护差分数组 c c c来求区间 g c d gcd gcd。
注意点:
- 对区间 [ l , r ] [l,r] [l,r]加上 x x x时, c [ l ] + = x , c [ r + 1 ] − = x ( 差 分 数 组 的 性 质 ) c[l]+=x,c[r+1]-=x(差分数组的性质) c[l]+=x,c[r+1]−=x(差分数组的性质),要特判 r + 1 < = n 。 r+1<=n。 r+1<=n。
- 当查询为 2 , l , r 2,l,r 2,l,r时,我们得查询区间 [ l + 1 , r ] [l+1,r] [l+1,r]
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5;
int a[N], mx[N<<2], sum[N<<2], G[N<<2];
//分别为a数组;线段树维护的最大值数组,和数组,gcd数组。
//向上传递函数
void pushup(int num) {
mx[num] = max(mx[num<<1], mx[num<<1|1]);
G[num] = __gcd(G[num<<1], G[num<<1|1]);
sum[num] = sum[num<<1|1] + sum[num<<1];
}
//构建线段树
void build(int l, int r, int num) {
if(l == r) mx[num] = G[num] = abs(a[l]), sum[num] = a[l];
else {
int mid = l + r >> 1;
build(l, mid, num<<1);
build(mid+1, r, num<<1|1);
pushup(num);
}
}
//在区间[l,r]加上val。
void updata(int l, int r, int num, int pos, int val) {
if(l == r) {
a[l] += val;
sum[num] = a[l];
mx[num] = G[num] = abs(a[l]);
}
else {
int mid = l + r >> 1;
if(pos <= mid) updata(l, mid, num<<1, pos, val);
else updata(mid+1, r, num<<1|1, pos, val);
pushup(num);
}
}
//查询和(a[en])
int query_sum(int l, int r, int st, int en, int num) {
int mid = l + r >> 1;
if(st <= l && r <= en) return sum[num];
else if(en <= mid) return query_sum(l, mid, st, en, num<<1);
else if(st > mid) return query_sum(mid+1, r, st, en, num<<1|1);
else return query_sum(l, mid, st, en, num<<1) + query_sum(mid+1, r, st, en, num<<1|1);
}
//查询gcd
int query_G(int l, int r, int st, int en, int num) {
int mid = l + r >> 1;
if(st <= l && r <= en) return G[num];
else if(en <= mid) return query_G(l, mid, st, en, num<<1);
else if(st > mid) return query_G(mid+1, r, st, en, num<<1|1);
else return __gcd(query_G(l, mid, st, en, num<<1), query_G(mid+1, r, st, en, num<<1|1));
}
//查询最大值
int query_mx(int l, int r, int st, int en, int num) {
int mid = l + r >> 1;
if(st <= l && r <= en) return mx[num];
else if(en <= mid) return query_mx(l, mid, st, en, num<<1);
else if(st > mid) return query_mx(mid+1, r, st, en, num<<1|1);
else return max(query_mx(l, mid, st, en, num<<1), query_mx(mid+1, r, st, en, num<<1|1));
}
int main() {
// freopen("in.txt", "r", stdin);
int n, m, f, l, r, x;
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=n; i; i--) a[i] -= a[i-1];
build(1, n, 1);
for(int i=1; i<=m; i++) {
scanf("%d%d%d", &f, &l, &r);
if(f == 1) {
scanf("%d", &x);
updata(1, n, 1, l, x);
if(r < n) updata(1, n, 1, r+1, -x);//注意点1.
}
else if(f == 2) {
if(l == r) printf("0\n");
else printf("%d\n", query_mx(1, n, l+1, r, 1));//注意点2.
}
else {
int t = query_sum(1, n, 1, l, 1);
if(l == r) printf("%d\n", t);
else printf("%d\n", __gcd(t, query_G(1, n, l+1, r, 1)));
}
}
return 0;
}