传送门
题意
给定长度为(n)的序列(a),(m)条指令,每个指令为下面两种操作之一
- ((C , l , r , d)) 将(a[l],a[l+1]dots a[r]) 区间都加上一个数
- ((Q , l , r)) 询问(a[l]dots a[r])的最大公约数
数据范围
(1leq Nleq 5 imes 10^{5})
(1leq Mleq 10^{5})
题解
- 更相减损求(gcd)
- (gcd(x,y)=gcd(x,y-x)),扩展到三个的情况(gcd(x,y,z)=gcd(x,y-x,z-y))
- (gcd(a_{1},a_{2},a_{3},a_{4},dots ,a_{n}) = gcd(a_{1},a_{2}-a_{1},a_{3}-a_{2},a_{4}-a_{3},dots ,a_{n}-a_{n-1}))
- 首先证明:
- (d=gcd(a_{1},a_{2},a_{3},a_{4},dots ,a_{n}) leq gcd(a_{1},a_{2}-a_{1},a_{3}-a_{2},a_{4}-a_{3},dots ,a_{n}-a_{n-1}))
- 证明左边的(gcd)一定是右边的某一个公约数
- (d)能整除(a_{1})
- (d)能整除(a_2),所以必定能整除(a_{2}-a_{1}),类推
- (gcd(a_{1},a_{2},a_{3},a_{4},dots ,a_{n}) geq gcd(a_{1},a_{2}-a_{1},a_{3}-a_{2},a_{4}-a_{3},dots ,a_{n}-a_{n-1})=d)
- (d)能整除(a_{1})
- (d)能整除(a_2-a_{1})因为能整除(a_{1}),所以必然能整除((a_{2}-a_{1})+a_{1}),类推
线段树只需要实现区间修改、单点查询
- (d)能整除(a_2-a_{1})因为能整除(a_{1}),所以必然能整除((a_{2}-a_{1})+a_{1}),类推
- 首先证明:
- 线段树属性为维护原序列的差分和即差分gcd
- 递归操作查询区间([l,r])的最大公约数可以得到:(gcd( a[ l ] , gcd(b[ l + 1 ] sim b[ r ] ) ))
- 维护的是差分序列所以可能会有负数,取绝对值即可
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define ll long long
#define close ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
const int N=5e5+10;
int n,m;
ll a[N];
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
struct node
{
int l,r;
ll sum,gcd;
#define l(u) u*2
#define r(u) u*2+1
}tr[N*4];
void pushup(node &root,node &left,node &right)
{
root.sum=left.sum+right.sum;
root.gcd = gcd(left.gcd,right.gcd);
}
void pushup(int u)
{
pushup(tr[u],tr[l(u)],tr[r(u)]);
}
void build(int u,int l,int r)
{
if(l==r)
{
ll tmp=a[r]-a[r-1];
tr[u]={l,r,tmp,tmp};
}
else
{
tr[u].l=l,tr[u].r=r;
int mid=l+r>>1;
build(l(u),l,mid);
build(r(u),mid+1,r);
pushup(u);
}
}
void modify(int u,int id,ll x)
{
if(tr[u].l == id && tr[u].r == id)
{
ll tmp = tr[u].sum+x;
tr[u] = {id,id,tmp,tmp};
}
else
{
int mid = tr[u].l+tr[u].r>>1;
if(id <= mid) modify(l(u),id,x);
else modify(r(u),id,x);
pushup(u);
}
}
node query(int u,int l,int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid=tr[u].l+tr[u].r>>1;
if(r <= mid) return query(l(u),l,r);
else if(l>mid) return query(r(u),l,r);
else
{
node left=query(l(u),l,r);
node right=query(r(u),l,r);
node res;
pushup(res,left,right);
return res;
}
}
void solve()
{
cin>>n>>m;
rep(i,1,n+1)
cin>>a[i];
build(1,1,n);
char op[2];
int l,r;
ll d;
while(m--)
{
cin>>op>>l>>r;
if(op[0]=='C')
{
cin>>d;
modify(1,l,d);
if(r+1<=n) modify(1,r+1,-d);
}
else
{
node left=query(1,1,l);
node right=query(1,l+1,r);
cout<<abs(gcd(left.sum,right.gcd))<<endl;
}
}
}
int main()
{
close;
solve();
}