线段树维护区间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\) 的性质

首先,根据更相减损法,我们有

\[\gcd(a,b)=\gcd(a,a-b)=\gcd(a-b,b) \]

然后我们对于这个式子进行扩展,有:

\[\gcd(a,b,c)=gcd(a,b-a,c-b) \]

所以我们可以得到:

\[\gcd(a[1],a[2],……a[n])=\gcd(a[1],a[2]-a[1],……a[n]-a[n-1]) \]

我们考虑对原数组进行差分,设差分数组为 \(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;
}
posted @ 2022-09-18 08:55  respect_lowsmile  阅读(1353)  评论(0)    收藏  举报