题目链接
链接: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] 区间里所有贝壳颜色值的最大公约数。
题目要求3个操作分别是:
维护区间修改 ,维护区间最大公因数(GCD) ,维护相邻差值 的最大值
- 区间和 = 左区间和 + 右区间和
- 区间GCD = gcd( 左区间GCD , 右区间GCD );
- 差值最大值=max(左区间差值最大值,右区间差值最大值)
如何维护区间GCD ,有一下公式
gcd(a,b)=gcd(a,b−a)
gcd(a,b,c)=gcd(a,b−a,c−b)
gcd(a1,a2,⋯,an)=gcd(a1,a2−a1,a3−a2,⋯,an−an−1)
观察公式 a1, a2-a1,⋯,an−an−1 为 差分数列
不懂差分的看这差分性质
因此用 差分数列 建线段树
1. 区间修改 根据差分性质 变成了单点修改
2. 区间gcd 求[L,R]的 最大公因数, 分成求 [1,L] 差分数列的区间和sum 与 [L+1,R]的gcd
3. 差值最大值,直接维护差值绝对值的最大值
下面的两个模板,在build 和update函数中 提供了两种 维护区间gcd方法
| | 双竖线 代表 绝对值 , 因为差分过程 可能会出现负数
1.gcd(a1,a2,⋯,an)=gcd(a1, | a2−a1 | , | a3−a2 | ,⋯, | an−an−1 | )
2.gcd(a1,a2,⋯,an)= | gcd(a1,a2−a1,a3−a2,⋯,an−an−1) |
//模板1
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
typedef struct
{
ll sum; // 维护差分数列前缀和
ll gcd; // 维护差分数列 最大公因数
ll mx; // 维护相邻差值的 最大值
}stu;
stu tr[N<<2];
ll a[N],b[N];
/*
gcd(a,b)=gcd(a,b−a)
gcd(a,b,c)=gcd(a,b−a,c−b)
gcd(a1,a2,⋯,an)=gcd(a1,a2−a1,a3−a2,⋯,an−an−1)
*/
void pushup(ll p)
{
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
tr[p].gcd=__gcd(tr[p<<1].gcd,tr[p<<1|1].gcd); // __gcd() 为c++ 自带最大公因数函数
tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx);
}
void build(ll p,ll l,ll r)
{
if(l==r)
{
tr[p].sum=b[l];
tr[p].gcd=abs(b[l]);
tr[p].mx=abs(b[l]);
return;
}
ll mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void update(ll p,ll l,ll r,ll pos,ll x)
{
if(l==r)
{
tr[p].sum+=x;
tr[p].gcd=abs(tr[p].sum);
tr[p].mx=abs(tr[p].sum);
return ;
}
ll mid=(l+r)>>1;
if(pos<=mid)
update(p<<1,l,mid,pos