链接:​​https://ac.nowcoder.com/acm/contest/949/H​​​​

来源:牛客网


时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 524288K,其他语言1048576K

64bit IO Format: %lld

题目描述

小阳手中一共有 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≤n

先献上官方题解:

牛客小白月赛16  小阳的贝壳(线段树维护差分数组)_数组

感觉我对差分理解的还不够深刻呀!

百度一波差分性质:

差分就是将数列中的每一项分别与前一项数做差,例如:

一个序列1 2 5 4 7 3,差分后得到1 1 3 -1 3 -4 -3

这里注意得到的差分序列第一个数和原来的第一个数一样(相当于第一个数减0)

差分序列最后比原序列多一个数(相当于0减最后一个数)

性质:

1、差分序列求前缀和可得原序列

2、将原序列区间[L,R]中的元素全部+1,可以转化操作为差分序列L处+1,R+1处-1

3、按照性质2得到,每次修改原序列一个区间+1,那么每次差分序列修改处增加的和减少的相同

差分性质来自




那么结合gcd的性质和差分的性质就可以就解决区间gcd的问题了

学到了:

1、差分数组维护区间gcd

2、线段树维护差分数组

至于操作2是多余的一个功能,但是我还是不会,居然也是和差分数组有关。

答案就是差分数组内最大值最小值的 绝对值最大的一个

3、维护区间最大相邻绝对值

学到了好多呀。。

#include<bits/stdc++.h>
using namespace std;
#define ls id<<1
#define rs id<<1|1
const int N=1e5+10;
int gcd[N*4],mx[N*4],mi[N*4],T[N*4],n,m,a[N];
void up(int id,int l,int r,int pos,int val)
{
if(l==r)
{
mx[id]+=val;
mi[id]=gcd[id]=mx[id];
gcd[id]=abs(gcd[id]);///绝对值??
return ;
}
int mid=l+r>>1;
if(pos<=mid)up(ls,l,mid,pos,val);
else up(rs,mid+1,r,pos,val);
mx[id]=max(mx[ls],mx[rs]);
mi[id]=min(mi[ls],mi[rs]);
gcd[id]=__gcd(gcd[ls],gcd[rs]);
}
void up2(int id,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr){
T[id]+=v;
return;
}
int mid=l+r>>1;
if(ql<=mid) up2(ls,l,mid,ql,qr,v);
if(qr>mid) up2(rs,mid+1,r,ql,qr,v);
}
int qu2(int id,int l,int r,int pos)
{
if(l==r) return T[id];
int res=T[id];
int mid=l+r>>1;
if(pos<=mid) res+=qu2(ls,l,mid,pos);
else res+=qu2(rs,mid+1,r,pos);
return res;
}
int qu(int id,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr){
return max(abs(mx[id]),abs(mi[id]));
}
int mid=l+r>>1;
int res=0;
if(ql<=mid) res=max(res,qu(ls,l,mid,ql,qr));
if(qr>mid) res=max(res,qu(rs,mid+1,r,ql,qr));
return res;
}
int qugcd(int id,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return gcd[id];
int mid=l+r>>1;
if(qr<=mid) return qugcd(ls,l,mid,ql,qr);
else if(ql>mid) return qugcd(rs,mid+1,r,ql,qr);
else{
int t1=qugcd(ls,l,mid,ql,qr);
int t2=qugcd(rs,mid+1,r,ql,qr);
return __gcd(t1,t2);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
up(1,1,n,i,a[i]-a[i-1]);
up2(1,1,n,i,i,a[i]);
}
int ans;
while(m--){
int ty,l,r;
scanf("%d%d%d",&ty,&l,&r);
if(ty==1){
int x;
scanf("%d",&x);
up(1,1,n,l,x);
if(r+1<=n) up(1,1,n,r+1,-x);
up2(1,1,n,l,r,x);
}
else if(ty==2){
if(l==r) printf("0\n");
else{
ans=qu(1,1,n,l+1,r);//因为是差分数组,所以l要加1
printf("%d\n",ans);
}
}
else{
int v=qu2(1,1,n,l);
if(l==r) printf("%d\n",v);
else{
int gc=qugcd(1,1,n,l+1,r);
printf("%d\n",__gcd(gc,v));//左端点的值gcd上差分数组内的gcd
}
}
}
}