题目传送门
题目描述
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 col_i 。现在小阳有 3 种操作:
1 l r x:给 [l,r] 区间里所有贝壳的颜色值加上 x 。
2 l r:询问 [l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=r 输出 0)。
3 l r :询问 [l,r] 区间里所有贝壳颜色值的最大公约数。
输入描述:
第一行输入两个正整数 n,m,分别表示贝壳个数和操作个数。
第二行输入 n 个数 colicol_icoli,表示每个贝壳的初始颜色。
第三到第 m+2 行,每行第一个数为 opt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述:
共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。
输入
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
思路
线段树维护差分数组,差分数组f[i]等于原数组相邻两个元素之差,f [i] = col[i]-col[i-1],设数组下标从1开始,那么f [1]=col [1],f [2]=col [2] - col [1]。
本题目用到的差分数组性质:
- 原数组的第 i 项为差分数组的前缀和,即 c o l [ i ] = f [ 1 ] + f [ 2 ] + f [ 3 ] + . . . + f [ i ] col[i]=f[1]+f[2]+f[3]+...+f[i] col[i]=f[1]+f[2]+f[3]+...+f[i]
- 原数组区间修改时,假设[l,r]要加上x,差分数组对应修改为两次单点修改: f [ l ] + x f[l]+x f[l]+x 和 f [ r + 1 ] − x f[r+1]-x f[r+1]−x ,这样求 c o l [ i ] i ∈ [ l , r ] col[i]\space i∈[l,r] col[i] i∈[l,r]时,会保证加上f[l]加过的x,求之后的元素 c o l [ i ] i ∈ ( r , + ∞ ) col[i]\space i∈(r,+\infty) col[i] i∈(r,+∞)时,会用f[r+1]减去的x与f[l]加过的x相抵消。
其他性质:差分数组
本题解法:
-
第一种操作,就使用线段树进行差分数组的单点修改 : f [l]+x 和 f [r+1]-x。
-
第二种操作,[ l , r ]相邻元素差的绝对值的最大值,则是差分数组 [ l+1 , r ] 的绝对值的区间最大值,可以用线段树维护绝对值的最大值。
-
第三种操作,求 [ l , r ] 的gcd。同更相减损法的原理, g c d ( c o l [ i ] , c o l [ i + 1 ] ) = g c d ( c o l [ i ] , ∣ c o l [ i + 1 ] − c o l [ i ] ∣ ) gcd(col[i],col[i+1])=gcd(col[i],|col[i+1]-col[i]|) gcd(col[i],col[i+1])=gcd(col[i],∣col[i+1]−col[i]∣),后者的 ∣ c o l [ i + 1 ] − c o l [ i ] ∣ |col[i+1]-col[i]| ∣col[i+1]−col[i]∣就是f[i+1]的绝对值,因此需要求解[l,r]的gcd,可以使用线段树维护差分数组 [ l+1 , r ] 的gcd,再与col [ l ]计 算一次gcd。注意特判 l==r 的情况,l 等于r时,l+1会大于r的!!
代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100100
struct node{
int l,r;
int g,ma,sum;
}tree[MAXN<<2];
int num[MAXN];
int arr[MAXN];
int gcd(int a,int b){
return !b ? a : gcd(b,a%b);
}
void pushup(int d){
tree[d].sum=tree[d<<1].sum+tree[d<<1|1].sum;
tree[d].ma=max(tree[d<<1].ma,tree[d<<1|1].ma);
tree[d].g=gcd(tree[d<<1].g,tree[d<<1|1].g);
}
void build(int d,int l,int r){
tree[d].l=l,tree[d].r=r;
if (l==r){
tree[d].sum=arr[l];
tree[d].g=tree[d].ma=abs(arr[l]);
return;
}
int mid=l+r>>1,lson=d<<1,rson=d<<1|1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(d);
}
void update(int d,int pos,int val){
if (tree[d].l==tree[d].r && tree[d].l==pos){
tree[d].sum+=val;
tree[d].ma=tree[d].g=abs(tree[d].sum);
return;
}
int mid=tree[d].l+tree[d].r>>1,lson=d<<1,rson=d<<1|1;
if (pos<=mid){
update(lson,pos,val);
}else {
update(rson,pos,val);
}
pushup(d);
}
int query(int d,int l,int r,int tag){
if (tree[d].l==l && tree[d].r==r){
return tag==2 ?tree[d].ma:tree[d].g;
}
int mid=tree[d].l+tree[d].r>>1,lson=d<<1,rson=d<<1|1;
if (r<=mid){
return query(lson,l,r,tag);
}else if (l>mid){
return query(rson,l,r,tag);
}else {
return tag==2 ? max(query(lson,l,mid,tag),query(rson,mid+1,r,tag))
: gcd(query(lson,l,mid,tag),query(rson,mid+1,r,tag));
}
}
int sum_query(int d,int l,int r){
if (tree[d].l==l && tree[d].r==r){
return tree[d].sum;
}
int mid=tree[d].l+tree[d].r>>1,lson=d<<1,rson=d<<1|1;
if (mid>=r){
return sum_query(lson,l,r);
}else if (mid<l){
return sum_query(rson,l,r);
}else {
return sum_query(lson,l,mid)+sum_query(rson,mid+1,r);
}
}
int main (){
int n,m,opt,l,r,x;
while (scanf("%d%d",&n,&m)!=EOF){
for (int i=1;i<=n;i++){
scanf("%d",&num[i]);
arr[i]=num[i]-num[i-1];
}
build(1,1,n);
for (int i=0;i<m;i++){
scanf("%d",&opt);
if (opt==1){
scanf("%d%d%d",&l,&r,&x);
update(1,l,x);
update(1,r+1,-x);
}else {
scanf("%d%d",&l,&r);
if (opt==2){
l==r?puts("0"):printf("%d\n",query(1,l+1,r,opt));
}else {
l==r?printf("%d\n",sum_query(1,1,l)):printf("%d\n",gcd(sum_query(1,1,l),query(1,l+1,r,opt)));
}
}
}
}
return 0;
}