链接:https://ac.nowcoder.com/acm/contest/949/H
来源:牛客网
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 coli i 。现在小阳有 3 种操作:
1 l r x:给 [l,r] 区间里所有贝壳的颜色值加上 xxx 。
2 l r:询问 [l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l=rl = rl=r 输出 0)。
3 l r :询问 [l,r] 区间里所有贝壳颜色值的最大公约数。
第一行输入两个正整数 n,mn,mn,m,分别表示贝壳个数和操作个数。
第二行输入 nnn 个数 colicol_icoli,表示每个贝壳的初始颜色。
第三到第 m+2m + 2m+2 行,每行第一个数为 optoptopt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述:
共 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≤100000
1≤coli≤1000
1≤opt≤3,1≤l≤r≤n
线段树操作集合问题,我们发现有三种操作,第一种 区间更新,l-r 区间每个数的值 +x 第二种 相邻最大值,即区间各数之间相邻的最大值的绝对值,第三种,求区间的gcd。
第一次碰到这种问题,往常的区间更新都是借用一个lazy数组来完成,但考虑到这个题还有相邻之间的距离最大值,还有区间gcd,显然用lazy肯定不行,通过这个题我学到了差分数组的思想。
差分数组:即数组中存放的不是原来的值,而是当前的数和前一个数的差值:
举个例子: 原数组 1 3 5 10 8 ——> 差分数组 1 2 2 5 -2
我们还可以发现一个规律,对差分数组求前缀和就是原来的第n项。
不难发现,差分数组的值就是相邻之间的差值,所以可以用线段树维护差分数组的绝对值的最大值。
关于区间更新:在差分数组中可以这样实现,举个例子:【3,5】这个区间内的每一个数都+2,因为差分数组中存放的是一个差值,所以我们在3这个位置+2,在6这个位置-2 就可以实现【3,5】每个数都+2了(可以自己带一组数据试试)。
再谈gcd问题 gcd(a,b)= gcd(a,b-a)同样可以用差分数组来实现,所以其他的不变,只需要把原来建树的普通数组改成差分数组即可,其他操作都用线段树来实现。AC代码:
#include
#include
#include
using namespace std;
const int _max = 1e5+50;
int a[_max];
struct node//线段树中存放的值
{
int sum,mx,gd;
}tree[_max<<2];
int gcd(int a,int b)//欧几里得求gcd
{
return b==0?a:gcd(b,a%b);
}
void pushup(int k)//线段树常规更新函数
{
tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
tree[k].gd=gcd(tree[k<<1].gd,tree[k<<1|1].gd);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void build(int node,int l,int r)
{
if(l==r)
{
tree[node].mx=abs(a[l]);//因为要求的是绝对值,所以这里维护一下绝对值。
tree[node].gd=abs(a[l]);//gcd同样存一下绝对值
tree[node].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
pushup(node);
}
void update(int node,int l,int r,int idx,int val)//区间更新
{
if(l==r)
{
tree[node].sum+=val;
tree[node].mx=tree[node].gd=abs(tree[node].sum);
return;
}
int mid=(l+r)>>1;
if(idx<=mid)
update(node<<1,l,mid,idx,val);
else
update(node<<1|1,mid+1,r,idx,val);
pushup(node);
}
int query_sum(int node,int l,int r,int start,int end)//区间求和,维护一下L==R的情况
{
if(l>=start&&r<=end)
return tree[node].sum;
int mid=(l+r)>>1;
int ans=0;
if(start<=mid)
ans+=query_sum(node<<1,l,mid,start,end);
if(end>mid)
ans+=query_sum(node<<1|1,mid+1,r,start,end);
return ans;
}
int query_max(int node,int l,int r,int start,int end)//区间最大值
{
if(l>=start&&r<=end)
return tree[node].mx;
int mid=(l+r)>>1;
int ans=-1;
if(start<=mid)
ans=max(query_max(node<<1,l,mid,start,end),ans);
if(end>mid)
ans=max(query_max(node<<1|1,mid+1,r,start,end),ans);
return ans;
}
int query_gcd(int node,int l,int r,int start,int end)//区间gcd
{
if(l>=start&&r<=end)
return tree[node].gd;
int mid=(l+r)>>1;
int ans=0;
if(start<=mid)
ans=gcd(query_gcd(node<<1,l,mid,start,end),ans);
if(end>mid)
ans=gcd(query_gcd(node<<1|1,mid+1,r,start,end),ans);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
a[0]=0;
for(int i=n;i>=1;i--)
a[i]-=a[i-1];
build(1,1,n);
while(m--)
{
int op,l,r;
cin>>op>>l>>r;
if(op==1)
{
int x;
cin>>x;
update(1,1,n,l,x);//l +x r+1 -x
if(r<n)
update(1,1,n,r+1,-x);
}
else if(op==2)
{
if(l==r)
cout<<0<<endl;
else
{
int ans=query_max(1,1,n,l+1,r);
cout<<ans<<endl;
}
}
else
{
int ans=abs(query_sum(1,1,n,1,l));
if(l==r)
cout<<ans<<endl;
else
{
ans=gcd(ans,query_gcd(1,1,n,l+1,r));
cout<<ans<<endl;
}
}
}
//system("pause");
return 0;
}