线段树维护区间gcd
线段树应用
线段树维护区间 \(gcd\)
Acwing 246. 区间最大公约数
题目描述:
给定一个长度为 \(N\) 的数列 \(A\),以及 \(M\) 条指令,每条指令可能是以下两种之一:
C l r d,表示把 \(A[l],A[l+1],…,A[r]\) 都加上 \(d\)。
Q l r,表示询问 \(A[l],A[l+1],…,A[r]\) 的最大公约数(\(GCD\))。
对于每个询问,输出一个整数表示答案。
输入格式:
第一行两个整数 \(N,M\)。
第二行 \(N\) 个整数 \(A[i]\)。
接下来 \(M\) 行表示 \(M\) 条指令,每条指令的格式如题目描述所示。
输出格式:
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围:
\(N≤500000,M≤100000\),
\(1≤A[i]≤1018,\)
\(|d|≤1018\)
输入样例:
5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4
输出样例:
1
2
4
\(solution\)
如果只要求区间查询的话,我们的区间 \(gcd\) 就等于每个小区间的 \(gcd\) 的 \(gcd\),可以用 \(ST\) 表或者线段树直接维护。
但是这个题多了修改操作,我们就不能直接维护区间 \(gcd\) 了,因为它不支持区间修改,很显然,一个区间的每个数都加上一个数 \(x\),那么这个区间的最大公约数显然不一定是 \(x+len\)。
我们考虑 \(gcd\) 的性质
首先,根据更相减损法,我们有
然后我们对于这个式子进行扩展,有:
所以我们可以得到:
我们考虑对原数组进行差分,设差分数组为 \(cf\),则区间 \([l,r]\) 的 \(gcd\) 就等于 \(\gcd(a[l],\gcd(b[l+1],b[l+2],……b[r])\ )\)
我们的 \(a[l]\) 又可以通过求 \(b[1] -> b[l]\) 的前缀和求得。
所以区间 \([l,r]\) 的 \(gcd\) 就等于 \(\gcd\ (\) 区间 \([1,l]\)的 \(sum\),区间\([l+1,r]\)的 \(gcd\) \()\)
区间修改的问题也相应地得到了解决,修改区间 \([l,r]\),我们只需要修改 \(cf[l]\) 和 \(cf[r+1]\) 就可以了。也就是说我们把无法支持的区间修改转化为了可以支持的单点修改。
这里还有几个小细节,首先,因为我们在进行差分的时候改变的是 \(cf[r+1]\),所以可能会出现越界的情况,我们要特判掉。
然后,有可能区间 \(gcd\) 求出来之后是负数,但是我们规定 \(gcd\) 一定是正的,因为我们有 \(gcd(a,b)=gcd(a,-b)\) ,所以我们直接输出答案的绝对值即可,但是千万不能修改 \(cf\) 数组,否则会改变原来的数值。
\(code\)
#include<iostream>
#define lc now<<1
#define rc now<<1|1
#define int long long
using namespace std;
const int N=5e5+5;
struct node
{
int sum,d;
};
node tree[N<<2];
int cf[N],a[N];
int n,m;
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9')
{ if(ch=='-') w=-1; ch=getchar();}
while(ch>='0'&&ch<='9')
{ s=s*10+ch-'0'; ch=getchar();}
return s*w;
}
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void push_up(int now)
{
tree[now].sum=tree[lc].sum+tree[rc].sum;
tree[now].d=gcd(tree[lc].d,tree[rc].d);
}
void build(int now,int l,int r)
{
if(l==r)
{
tree[now].sum=tree[now].d=cf[l];
return ;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
push_up(now);
}
void update(int now,int l,int r,int pos,int v)
{
if(l==r)
{
tree[now].sum+=v;
tree[now].d+=v;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(lc,l,mid,pos,v);
else update(rc,mid+1,r,pos,v);
push_up(now);
}
node merga(node a,node b)
{
node res;
res.sum=a.sum+b.sum;
res.d=gcd(a.d,b.d);
return res;
}
node query(int now,int l,int r,int L,int R)
{
if(l>=L&&r<=R)
return tree[now];
int mid=(l+r)>>1;
if(R<=mid) return query(lc,l,mid,L,R);
if(L>mid) return query(rc,mid+1,r,L,R);
return merga(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
}
signed main()
{
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read(),cf[i]=a[i]-a[i-1];
build(1,1,n);
for(int i=1;i<=m;++i)
{
char ch;
cin>>ch;
if(ch=='C')
{
int l=read(),r=read(),d=read();
update(1,1,n,l,d);
if(r<n) update(1,1,n,r+1,-d);
}
if(ch=='Q')
{
int l=read(),r=read();
node Lres=query(1,1,n,1,l);
node Rres=(node){0,0};
if(l+1<=r) Rres=query(1,1,n,l+1,r);
cout<<abs(gcd(Lres.sum, Rres.d)) <<endl;
}
}
return 0;
}

浙公网安备 33010602011771号