题目描述
小阳手中一共有 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
思路:
首先介绍差分数组:
对于数组a: a1、a2、a3、a4、a5、、、an;
我们建立一个数组f,f是a的相邻两项的差,
f0=0;
f1=a1;
f2=a2-a1;
f3=a3-a2;
f4=a4-a3;
f5=a5-a4;
…
fn=an-a(n-1);
那么an=f的前n项和,在a的一个区间[l,r]加x时,只需要修改f[l]和f[r+1],求ai时通过f的前缀和来求解,使用树状数组或者线段数来维护f的区间和,就能将时间复杂度控制在O(logn)。
对于本题我们建立数组f来记录相邻coli的差值,用四个线段数(也可以用树状数组,后面有讲解)分别维护f的区间和、区间最大值、区间最小值、区间gcd。
对操作1,更新f[l]和f[r+1];
对操作2,查询f区间的最小值和最大值,两个取绝对值后最大的即是所求;
对操作3,求gcd(a[l],k);a[l]通过f的前缀和求出,k=f的[l+1,r]区间gcd;
对操作3的证明:
gcd(a,b)=gcd(a,b-a);
gcd(a,b,c)=gcd(a,b-a,c-b);
线段数实现参考代码:
#include <iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const int NINF=0xc0c0c0c0;
const int MAX_N=1e5+5;
const int MAX_M=500+5;
int n,q;
ll f[MAX_N+1];
ll dat0[(1<<17)*2+1],dat1[(1<<17)*2+1],dat2[(1<<17)*2+1],dat3[(1<<17)*2+1];
ll gcd(ll a,ll b){
if(b==0)return a;
return gcd(b,a%b);
}
void init(int k,int l,int r){
if(l==r){
dat0[k]=dat1[k]=dat2[k]=dat3[k]=f[l];
return;
}
init(k*2,l,(l+r)/2);
init(k*2+1,(l+r)/2+1,r);
dat0[k]=dat0[k*2]+dat0[k*2+1];
dat1[k]=min(dat1[k*2],dat1[k*2+1]);
dat2[k]=max(dat2[k*2],dat2[k*2+1]);
dat3[k]=gcd(dat3[k*2],dat3[k*2+1]);
}
void update(int i,int k,int l,int r){
if(l==r&&l==i){
dat0[k]=dat1[k]=dat2[k]=dat3[k]=f[l];
}
else if(i>=l&&i<=r){
update(i,k*2,l,(l+r)/2);
update(i,k*2+1,(l+r)/2+1,r);
dat0[k]=dat0[k*2]+dat0[k*2+1];
dat1[k]=min(dat1[k*2],dat1[k*2+1]);
dat2[k]=max(dat2[k*2],dat2[k*2+1]);
dat3[k]=gcd(dat3[k*2],dat3[k*2+1]);
}
}
ll res0,res1,res2,res3;
void query(int a,int b,int k,int l,int r){
if(a<=l&&b>=r){
res0+=dat0[k];
res1=min(res1,dat1[k]);
res2=max(res2,dat2[k]);
res3=gcd(res3,dat3[k]);
}
else if(b<l||a>r)return ;
else {
query(a,b,k*2,l,(l+r)/2);
query(a,b,k*2+1,(l+r)/2+1,r);
}
}
void get(int l,int r){
res0=0;res1=INF;res2=NINF;res3=f[r];
}
int main(){
scanf("%d%d",&n,&q);
int pre=0;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
f[i]=x-pre;
pre=x;
}
init(1,1,n);
int t,l,r;
ll x;
while(q--){
scanf("%d%d%d",&t,&l,&r);
if(t==1){
scanf("%lld",&x);
f[l]+=x;
update(l,1,1,n);
f[r+1]-=x;
update(r+1,1,1,n);
}
else if(t==2){
if(l==r)printf("%d\n",0);
else{
get(l,r);
query(l+1,r,1,1,n);
printf("%lld\n",max(abs(res1),abs(res2)));
}
}
else{
get(1,l);
query(1,l,1,1,n);
ll k=res0;
if(l==r)printf("%lld\n",k);
else {
get(l+1,r);
query(l+1,r,1,1,n);
printf("%lld\n",abs(gcd(k,res3)));
}
}
}
return 0;
}
树状数组实现参考代码:
#include <iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const int NINF=0xc0c0c0c0;
const int MAX_N=1e5+5;
const int MAX_M=500+5;
int n,q;
ll f[MAX_N+1];
ll bit0[MAX_N+1],bit1[MAX_N+1],bit2[MAX_N+1],bit3[MAX_N+1];
ll gcd(ll a,ll b){
if(b==0)return a;
return gcd(b,a%b);
}
ll sum(int i){
ll s=0;
while(i>0){
s+=bit0[i];
i-=i&-i;
}
return s;
}
void add(int i,ll x){
while(i<=n){
bit0[i]+=x;
i+=i&-i;
}
}
void update(int i){
while(i<=n){
bit1[i]=f[i];
bit2[i]=f[i];
bit3[i]=f[i];
for(int j=1;j<(i&-i);j<<=1){
bit1[i]=min(bit1[i],bit1[i-j]);
bit2[i]=max(bit2[i],bit2[i-j]);
bit3[i]=gcd(bit3[i],bit3[i-j]);
}
i+=i&-i;
}
}
vector<ll> query(int l,int r){
vector<ll> res(4);
res[1]=INF;
res[2]=NINF;
res[3]=f[r];
while(r>=l){
res[1]=min(res[1],f[r]);
res[2]=max(res[2],f[r]);
res[3]=gcd(res[3],f[r]);
r--;
while(r-(r&-r)>=l){
res[1]=min(res[1],bit1[r]);
res[2]=max(res[2],bit2[r]);
res[3]=gcd(res[3],bit3[r]);
r-=r&-r;
}
}
return res;
}
int main(){
scanf("%d%d",&n,&q);
int pre=0;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
f[i]=x-pre;
pre=x;
}
for(int i=1;i<=n;i++){
add(i,f[i]);
update(i);
}
int t,l,r;
ll x;
while(q--){
scanf("%d%d%d",&t,&l,&r);
if(t==1){
scanf("%lld",&x);
add(l,x);
add(r+1,-x);
f[l]+=x;
update(l);
f[r+1]-=x;
update(r+1);
}
else if(t==2){
if(l==r)printf("%lld\n",0);
else{
vector<ll> res=query(l+1,r);
printf("%lld\n",max(abs(res[1]),abs(res[2])));
}
}
else {
ll k=sum(l);
if(l==r)printf("%lld\n",k);
else printf("%lld\n",abs(gcd(k,query(l+1,r)[3])));
}
}
return 0;
}