【区间gcd】 IntervalGCD
传送门
题意
给定长度为\(n\)的序列\(a\),\(m\)条指令,每个指令为下面两种操作之一
- \((C , l , r , d)\) 将\(a[l],a[l+1]\dots a[r]\) 区间都加上一个数
- \((Q , l , r)\) 询问\(a[l]\dots a[r]\)的最大公约数
数据范围
\(1\leq N\leq 5\times 10^{5}\)
\(1\leq M\leq 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();
}