链接:https://ac.nowcoder.com/acm/contest/949/H
来源:牛客网
题目描述
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为coli 。现在小阳有 3 种操作:
1 l r x:给 [l,r] 区间里所有贝壳的颜色值加上 x 。
2 l r:询问 [l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=r 输出 0)。
3 l r :询问 [l,r] 区间里所有贝壳颜色值的最大公约数。
输入描述:
第一行输入两个正整数 n,m,分别表示贝壳个数和操作个数。
第二行输入 n 个数 coli,表示每个贝壳的初始颜色。
第三到第 m+2 行,每行第一个数为 opt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述:
共 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
分析:
求区间GCD,区间修改就没法像区间求和那样用懒标记了,这里就要用到差分数组。
gcd有性质:gcd(a, b) = gcd(a, b-a)
推广一下:gcd(a, b, c) = gcd(a, b-a, c-b) = gcd(a, gcd (b-a, c-b) )
那么结合差分数组,这区间 [L, R]的gcd为:
g
c
d
(
a
[
L
]
,
R
g
c
d
i
=
L
+
1
∣
f
[
i
]
∣
)
gcd(a[L],\begin{matrix} \scriptsize R\\ gcd \\ \scriptsize i=L+1 \end{matrix}|f[i]|)
gcd(a[L],Rgcdi=L+1∣f[i]∣)
由差分数组性质可知:
a
[
x
]
=
∑
i
=
1
x
f
[
i
]
a[x]=\sum_{i=1}^{x}f[i]
a[x]=i=1∑xf[i]
所以,最终公式为:
g
c
d
(
∑
i
=
1
L
f
[
i
]
,
R
g
c
d
i
=
L
+
1
∣
f
[
i
]
∣
)
gcd(\sum_{i=1}^{L}f[i],\begin{matrix} \scriptsize R\\ gcd \\ \scriptsize i=L+1 \end{matrix}|f[i]|)
gcd(i=1∑Lf[i],Rgcdi=L+1∣f[i]∣)
对于求 颜色值的差(取绝对值) 的最大值,直接就是差分数组取绝对值的最大值,即
R
m
a
x
i
=
L
+
1
∣
f
[
i
]
∣
\begin{matrix} \scriptsize R\\ max \\ \scriptsize i=L+1 \end{matrix}|f[i]|
Rmaxi=L+1∣f[i]∣
对于差分数组的区间修改,只需要修改 f[L] 和 f[R+1] 即可。
对于操作2 的 L+1 ~ R 的最值,操作3需要的1 ~ L的求和、L+1 ~ R的gcd,都可以用线段树来维护。
以下代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int maxn=1e5+50;
int n,m,col[maxn],f[maxn];
struct node
{
int sum;
int gcd;
int maxx;
node()
{
sum=0;
gcd=0;
maxx=0;
}
}t[4*maxn]; //线段树至少开4倍
int GCD(int a,int b)
{
int x=a,y=b,z;
while(y)
{
z=x%y;
x=y;
y=z;
}
return x;
}
void built(int rt,int l,int r) //建树
{
if(l==r)
{
t[rt].sum=f[l];
t[rt].gcd=abs(f[l]); //gcd 和 maxx均取绝对值
t[rt].maxx=abs(f[l]);
return;
}
int mid=(l+r)>>1;
built(rt<<1,l,mid);
built(rt<<1|1,mid+1,r);
t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
t[rt].gcd=GCD(t[rt<<1].gcd,t[rt<<1|1].gcd);
t[rt].maxx=max(t[rt<<1].maxx,t[rt<<1|1].maxx);
}
void updata(int rt,int l,int r,int a,int b) //单点更新
{
if(l==r)
{
t[rt].sum+=b; //注意此处的修改
t[rt].gcd=abs(t[rt].sum);
t[rt].maxx=abs(t[rt].sum);
return;
}
int mid=(l+r)>>1;
if(a<=mid)
updata(rt<<1,l,mid,a,b);
else
updata(rt<<1|1,mid+1,r,a,b);
t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
t[rt].gcd=GCD(t[rt<<1].gcd,t[rt<<1|1].gcd);
t[rt].maxx=max(t[rt<<1].maxx,t[rt<<1|1].maxx);
}
int query_sum(int rt,int l,int r,int ql,int qr) //区间查询 sum
{
if(ql<=l&&r<=qr)
return t[rt].sum;
if(r<ql||l>qr)
return 0;
int mid=(l+r)>>1;
return query_sum(rt<<1,l,mid,ql,qr)+query_sum(rt<<1|1,mid+1,r,ql,qr);
}
int query_gcd(int rt,int l,int r,int ql,int qr) //区间查询 gcd
{
if(ql<=l&&r<=qr)
return t[rt].gcd;
if(r<ql||l>qr)
return 0; //任何数与0的gcd是其自身,所以区域不重合返回0
int mid=(l+r)>>1;
return GCD(query_gcd(rt<<1,l,mid,ql,qr),query_gcd(rt<<1|1,mid+1,r,ql,qr));
}
int query_maxx(int rt,int l,int r,int ql,int qr) //区间查询 maxx
{
if(ql<=l&&r<=qr)
return t[rt].maxx;
if(r<ql||l>qr)
return 0;
int mid=(l+r)>>1;
return max(query_maxx(rt<<1,l,mid,ql,qr),query_maxx(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&col[i]);
f[1]=col[1];
for(int i=2;i<=n;i++)
f[i]=col[i]-col[i-1];
built(1,1,n);
int opt,L,R,X;
while(m--)
{
int opt;
scanf("%d %d %d",&opt,&L,&R);
if(opt==1)
{
scanf("%d",&X);
updata(1,1,n,L,X);
if(R+1<=n)
updata(1,1,n,R+1,-X);
}
else if(opt==2)
{
if(L==R)
printf("0\n"); //特判
else
printf("%d\n",abs(query_maxx(1,1,n,L+1,R)));
}
else
{
if(L==R)
printf("%d\n",query_sum(1,1,n,1,L)); //特判
else
printf("%d\n",GCD(query_sum(1,1,n,1,L),query_gcd(1,1,n,L+1,R)));
}
}
return 0;
}