[线段树]校OJ-Interval GCD
Posted zero_orez6
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[线段树]校OJ-Interval GCD相关的知识,希望对你有一定的参考价值。
Interval GCD
题目大意
一个有n个数的序列,m次操作
每次操作包括:
1.
Q
l
r
Q\\ l\\ r
Q l r 查询从
l
l
l到
r
r
r中
a
[
l
]
,
a
[
l
+
1
]
.
.
.
a
[
r
]
a[l],a[l+1]...a[r]
a[l],a[l+1]...a[r]的最大公约数。
2.
C
l
r
d
C\\ l \\ r \\ d
C l r d表示从l到r每个数都加上d
思路
参考之前的更相减损法
g
c
d
(
a
,
b
)
=
g
c
d
(
x
,
b
−
a
)
gcd(a,b)=gcd(x,b-a)
gcd(a,b)=gcd(x,b−a)该算法对于多个数也同样适用。
GCD的问题解决了,那么如何求区间GCD呢?,一个个求显然太麻烦,观察到上面的更相减损法是差的形式,不妨构造一个新数组b,作为a的差分数组每次执行更改操作只需要
a
[
l
]
+
+
;
a
[
r
+
1
]
−
−
;
a[l]++;a[r+1]--;
a[l]++;a[r+1]−−;用线段树来维护b数组,这时,询问操作就变成了
g
c
d
(
a
[
l
]
,
a
s
k
(
1
,
l
+
1
,
r
)
)
gcd(a[l],ask(1,l+1,r))
gcd(a[l],ask(1,l+1,r)),另外,因为更改操作中a数组的值也需要实时更改,所以可以用树状数组来维护。
code
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=1000860;
struct tree{
LL l,r,gcdd;
#define l(o) t[o].l
#define r(o) t[o].r
#define gcdd(o) t[o].gcdd
}t[4*N];
LL n,a[N],b[N],c[N],m;
void build(LL bh,LL l,LL r)
{
l(bh)=l;
r(bh)=r;
if(l==r)
{
gcdd(bh)=b[l];
return ;
}
LL mid=(l+r)/2;
build(2*bh,l,mid);
build(2*bh+1,mid+1,r);
gcdd(bh)=__gcd(gcdd(bh*2),gcdd(bh*2+1));
}
void add(LL bh,LL x,LL sum)
{
if(l(bh)==r(bh))
{
gcdd(bh)+=sum;
return;
}
LL mid=(l(bh)+r(bh))/2;
if(x<=mid) add(bh*2,x,sum);
else add(bh*2+1,x,sum);
gcdd(bh)=__gcd(gcdd(bh*2),gcdd(bh*2+1));
}
LL ask(LL bh,LL ll,LL rr)
{
LL ans=0;
if(l(bh)>=ll&&rr>=r(bh))
{
return gcdd(bh);
}
LL mid=(l(bh)+r(bh))/2;
if(ll<=mid) ans=ask(bh*2,ll,rr);
if(rr>mid)
{
if(ans==0) ans=ask(bh*2+1,ll,rr);
else ans=__gcd(ans,ask(bh*2+1,ll,rr));
}
return ans;
}
LL lowbit(LL x)
{
return x&(-x);
}
void t_add(LL x,LL val)
{
for(;x<=n;x+=lowbit(x)) c[x]+=val;
}
LL t_ask(LL x)
{
LL ans=0;
while(x) ans+=c[x],x-=lowbit(x);
return ans;
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%lld%lld",&n,&m);
a[0]=0;
for(LL i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=a[i]-a[i-1];
}
for(LL i=1;i<=n;i++)
{
t_add(i,b[i]);
}
build(1,1,n);
for(LL i=1;i<=m;i++)
{
char c[10];
scanf("%s",c);
if(c[0]=='Q')
{
LL l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\\n",abs(__gcd(t_ask(l),ask(1,l+1,r))));
}
else{
LL l,r,d;
scanf("%lld%lld%lld",&l,&r,&d);
add(1,l,d);
if(r+1<=n) add(1,r+1,-d);
t_add(l,d);
if(r+1<=n) t_add(r+1,-d);
}
}
return 0;
}
以上是关于[线段树]校OJ-Interval GCD的主要内容,如果未能解决你的问题,请参考以下文章
hdu 5381 The sum of gcd(线段树+gcd)