线段树数组差分区间最大公约数

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树数组差分区间最大公约数相关的知识,希望对你有一定的参考价值。

思路:

做这道题真的是数据范围害死人,我就是没有注意到需要开到long long的范围,参数数据类型没写对,查bug查了一下午

这道题要求的是区间的最大公约数,显而易见可以用【线段树】
维护差分

注意一:

可以看到数据范围很大,需要开 l o n g   l o n g long \\ long long long,修改的值v需要是 l o n g   l o n g long \\ long long long,注意参数一定数据范围写对

注意二: g c d ( a , b ) = g c d ( b , a − b ) gcd(a,b) = gcd(b,a-b) gcd(a,b)=gcd(b,ab)

利用更相减损法,可以对之进行推广
g c d ( a 1 , a 2 , . . . , a n ) = g c d ( a 1 , a 2 − a 1 , a 3 − a 2 , . . . , a n − a n − 1 ) gcd(a_1,a_2,...,a_n) = gcd(a_1,a_2-a_1,a_3-a_2,...,a_n-a_{n-1}) gcd(a1,a2,...,an)=gcd(a1,a2a1,a3a2,...,anan1)

利用差分的性质可以把区间的加减变为单点的加减:
对一个区间 [ l , r ] [l,r] [l,r]求最大公约数就可以转化为求 w [ l ] w[l] w[l] [ l + 1 , r ] [l+1,r] [l+1,r]最大公约数的最大公约数, w [ l ] w[l] w[l]可以利用差分数组求区间 [ 1 , l ] [1,l] [1,l]差分的和求出

故节点信息需要记录:
区间sum(差分和)和区间最大公约数(注意记录的是差分的数)

注意三:

最后对区间进行加减后,求出的gcd可能为负数,我们需要对结果进行取绝对值,但是不能对节点进行取绝对值,否则节点信息就会改变了

注意四:

当询问区间是一个点时,需要判断是否超过n,判断是否越界

注意五:

读入的第一个为字符,连续多次读入那么第一个字符就会读入上一个回车字符,造成无法读入正确数据
需要读入%s,%s并不能读入回车,可以避免这样的问题

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5+5;

ll w[N];
int n,m;

struct node
{
	int l,r;
	ll sum,d;
}tr[N * 4];

ll gcd(ll a,ll b)
{
	return b ? gcd(b,a%b) : a;
}

void pushup(int u)
{
	tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
	tr[u].d = gcd(tr[u<<1].d,tr[u<<1|1].d);
}

void build(int u,int l,int r)
{
	if(l==r) 
	{
	    ll t = w[r]-w[r-1];
		tr[u] = {l,r,t,t};
		return ;
	}
	tr[u] = {l,r};
	int mid = tr[u].l + tr[u].r >> 1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
}

node query(int u,int l,int r)
{
	if(tr[u].l >= l && tr[u].r <= r) return tr[u];
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if(r <= mid) return query(u<<1,l,r);
		else if(l > mid) return query(u<<1|1,l,r);
		else 
		{
			node left = query(u<<1,l,r);
			node right = query(u<<1|1,l,r);
			node res;
			res.sum = left.sum + right.sum;
			res.d = gcd(left.d , right.d);
			return res;
		}
	}
}

void modify(int u,int x,ll v)
{
	if(tr[u].l == x && tr[u].r == x)
	{
		ll t = tr[u].sum + v;
		tr[u] = {x,x,t,t};
	}
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if(x<=mid) modify(u<<1,x,v);
		else modify(u<<1|1,x,v);
		pushup(u);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
	build(1,1,n);
	
	char c[2];
	int l,r; 
	ll d;

	while(m--)
	{
    	scanf("%s%d%d",c,&l,&r);
		if(*c=='Q')
		{
			node left = query(1,1,l);
			node right({0,0,0,0});
			if(l+1 <= r) right = query(1,l+1,r);
			printf("%lld\\n",abs(gcd(left.sum,right.d)));
		}
		else
		{
			scanf("%lld",&d);
			modify(1,l,d);
			if(r+1<=n) modify(1,r+1,-d);
		}
	}
	return 0;
}

以上是关于线段树数组差分区间最大公约数的主要内容,如果未能解决你的问题,请参考以下文章

[线段树 差分 区间转单点]区间最大公约数

AcWing 246. 区间最大公约数

线段树双tag+差分数组——cf1208E

2022蓝桥杯学习——5.树状数组和线段树差分

[LuoguP1438]无聊的数列(差分+线段树/树状数组)

Joy of Handcraft Gym - 102822J(线段树或差分)