题目链接:小阳的贝壳
题意
给你一个长度为n的数列,你能进行三个操作:
- 给区间[l,r]所有的值加上x
- 询问区间[l,r]内相邻两个数差值绝对值的最大值
- 询问[l,r]内所有数的最大公约数
题解
首先如果直接维护的话,步骤2和3很好实现,但是如果给区间增加一个数值,面对步骤3,我们很难在原有基础上改变,只能重新算,这样的时间复杂度和暴力无异。所以需要另辟蹊径。
倘若我们维护一个差分数列,情况就变得简单了许多。
设原数列: a 1 , a 2 , . . . , a n {a_1,a_2,...,a_n} a1,a2,...,an
那么差分数列为:
a 1 , a 2 − a 1 , a 3 − a 2 , . . . . . , a n − a n − 1 {a_1,a_2-a_1,a_3-a_2,.....,a_n-a_{n-1}} a1,a2−a1,a3−a2,.....,an−an−1
d 1 , d 2 , d 3 , . . . . . , d n {d_1,d_2,d_3,.....,d_n} d1,d2,d3,.....,dn
步骤1:影响的也只有 d l 和 d r + 1 {d_l和d_{r+1}} dl和dr+1,点修改即可
步骤2:维护一个区间最大值,然后查询区间[l+1,r]里的最大值。
步骤3:
易知 g c d ( a l , a l + 1 , . . . . . , a r ) = g c d ( a l , ∣ a l + 1 − a l ∣ , ∣ a l + 2 − a l + 1 ∣ , . . . . , ∣ a r − a r − 1 ∣ ) {gcd(a_l,a_{l+1},.....,a_{r})=gcd(a_l,\mid a_{l+1}-a_l \mid,\mid a_{l+2}-a_{l+1} \mid,....,\mid a_{r}-a_{r-1} \mid)} gcd(al,al+1,.....,ar)=gcd(al,∣al+1−al∣,∣al+2−al+1∣,....,∣ar−ar−1∣)
⇒ g c d ( a l , ∣ d l + 1 ∣ , ∣ d l + 2 ∣ , . . . . , ∣ d r ∣ ) {gcd(a_l,\mid d_{l+1} \mid,\mid d_{l+2} \mid,....,\mid d_{r} \mid)} gcd(al,∣dl+1∣,∣dl+2∣,....,∣dr∣)
⇒ g c d ( ∑ i = 1 l d i , ∣ d l + 1 ∣ , ∣ d l + 2 ∣ , . . . . , ∣ d r ∣ ) {gcd(\sum_{i=1}^ld_i,\mid d_{l+1} \mid,\mid d_{l+2} \mid,....,\mid d_{r} \mid)} gcd(∑i=1ldi,∣dl+1∣,∣dl+2∣,....,∣dr∣)
由上可知,我们再维护一个区间和和区间内的最大公约数即可。
综上所述,我们只需维护一个区间 d i {d_i } di和、区间 ∣ d i ∣ {\mid d_i \mid} ∣di∣最大值和区间 ∣ d i ∣ {\mid d_i \mid} ∣di∣最大公约数
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define lowbit(x) x&-x
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int col[maxn],d[maxn];
struct Tree
{
int summ,maxx,gcdd;
void init(int summ,int maxx,int gcdd)
{
this->summ = summ;
this->maxx = maxx;
this->gcdd = gcdd;
}
}tree[maxn<<2];
int gcd(int a,int b) {
return b==0 ?a:gcd(b,a%b); }
void pushup(int p)
{
tree[p].summ = tree[p*2].summ + tree[p*2+1].summ;
tree[p].maxx = max(tree[p*2].maxx, tree[p*2+1].maxx);
tree[p].gcdd = gcd(tree[p*2].gcdd, tree[p*2+1].gcdd);
}
void build(int p,int l,int r)
{
if(l==r) {
tree[p].summ=d[l]; tree[p].maxx=abs(d[l]); tree[p].gcdd=abs(d[l]); return ;}
int mid=l+(r-l)/2;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
pushup(p);
}
void add(int p,int l,int r,int pos,int v)
{
if(l==r) {
tree[p].summ+=v, tree[p].maxx=abs(tree[p].summ), tree[p].gcdd=abs(tree[p].summ); return ;}
int mid=l+(r-l)/2;
if(pos<=mid) add(p*2, l, mid, pos, v);
if(pos>mid) add(p*2+1, mid+1, r, pos, v);
pushup(p);
}
int query_gcd(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r) return tree[p].gcdd;
int mid=l+(r-l)/2;
int ans=0;
if(ql<=mid) ans = gcd(ans,query_gcd(p*2,l,mid,ql,qr));
if(qr>mid) ans = gcd(ans,query_gcd(p*2+1,mid+1,r,ql,qr));
return ans;
}
int query_sum(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r) return tree[p].summ;
int mid=l+(r-l)/2;
int ans=0;
if(ql<=mid) ans += query_sum(p*2,l,mid,ql,qr);
if(qr>mid) ans += query_sum(p*2+1,mid+1,r,ql,qr);
return ans;
}
int query_max(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r) return tree[p].maxx;
int mid=l+(r-l)/2;
int ans=0;
if(qr>mid) ans = max(ans,query_max(p*2+1,mid+1,r,ql,qr));
if(ql<=mid) ans = max(ans,query_max(p*2,l,mid,ql,qr));
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&col[i]);
d[i]=col[i]-col[i-1];
}
build(1, 1, n);
while(m--)
{
int op; scanf("%d",&op);
if(op==1)
{
int l,r,v; scanf("%d%d%d",&l,&r,&v);
add(1, 1, n, l, v);
if(r<n) add(1, 1, n, r+1, -v); //注意这里可能r+1>n所以必须特判
}
else if(op==2)
{
int l,r; scanf("%d%d",&l,&r);
printf("%d\n",query_max(1, 1, n, l+1, r));
}
else
{
int l,r; scanf("%d%d",&l,&r);
printf("%d\n",gcd(query_sum(1, 1, n, 1, l),query_gcd(1, 1, n, l+1, r)));
}
}
}