线段树数组差分区间最大公约数
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,a−b)
利用更相减损法,可以对之进行推广
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,a2−a1,a3−a2,...,an−an−1)
利用差分的性质可以把区间的加减变为单点的加减:
对一个区间 [ 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;
}
以上是关于线段树数组差分区间最大公约数的主要内容,如果未能解决你的问题,请参考以下文章