此题细节很多
1. g c d ( a , b ) = g c d ( a , b − a ) gcd(a,b)=gcd(a,b−a) gcd(a,b)=gcd(a,b−a)
在lyd的书里提到的这个性质叫更相减损术,可以推广到多个数的情况。
更相减损术其实是欧几里得算法的一个特例。即 g c d ( a − n b , b ) = g c d ( a , b ) gcd(a−nb,b)=gcd(a,b) gcd(a−nb,b)=gcd(a,b)。
推广:
(
a
,
b
,
c
)
=
(
(
a
,
b
)
,
(
b
,
c
)
)
=
(
(
a
,
b
−
a
)
,
(
b
,
c
−
b
)
)
=
(
a
,
b
−
a
,
b
,
c
−
b
)
(a,b,c)=((a,b),(b,c))=((a,b−a),(b,c−b))=(a,b-a,b,c-b)
(a,b,c)=((a,b),(b,c))=((a,b−a),(b,c−b))=(a,b−a,b,c−b)
由
于
(
b
−
a
,
b
)
=
(
a
,
b
−
a
)
所
以
(
a
,
b
,
c
)
=
(
a
,
b
−
a
,
c
−
b
)
由于(b−a,b)=(a,b−a)所以(a,b,c)=(a,b−a,c−b)
由于(b−a,b)=(a,b−a)所以(a,b,c)=(a,b−a,c−b)
再推广一下:
(
a
,
b
,
c
,
d
)
=
(
(
a
,
b
,
c
)
,
(
b
,
c
,
d
)
)
=
(
(
a
,
b
−
a
,
c
−
b
)
,
(
b
,
c
−
b
,
d
−
c
)
)
=
(a,b,c,d)=((a,b,c),(b,c,d))=((a,b-a,c-b),(b,c-b,d-c))=
(a,b,c,d)=((a,b,c),(b,c,d))=((a,b−a,c−b),(b,c−b,d−c))=
(
a
,
b
−
a
,
c
−
b
,
b
,
c
−
b
,
d
−
c
)
=
(
去
重
,
化
简
)
(
a
,
b
−
a
,
c
−
b
,
d
−
c
)
(a,b-a,c-b,b,c-b,d-c)=(去重,化简)(a,b-a,c-b,d-c)
(a,b−a,c−b,b,c−b,d−c)=(去重,化简)(a,b−a,c−b,d−c)
继续不耐烦地推广:
(
a
,
b
,
c
,
d
,
e
)
=
(
(
a
,
b
,
c
,
d
)
,
(
b
,
c
,
d
,
e
)
)
=
(
(
a
,
b
−
a
,
c
−
b
,
d
−
c
)
,
(
b
,
c
−
b
,
d
−
c
,
e
−
d
)
)
=
(a,b,c,d,e)=((a,b,c,d),(b,c,d,e))=((a,b-a,c-b,d-c),(b,c-b,d-c,e-d))=
(a,b,c,d,e)=((a,b,c,d),(b,c,d,e))=((a,b−a,c−b,d−c),(b,c−b,d−c,e−d))=
(
去
重
,
化
简
)
(
a
,
b
−
a
,
c
−
b
,
d
−
c
,
e
−
d
)
(去重,化简)(a,b-a,c-b,d-c,e-d)
(去重,化简)(a,b−a,c−b,d−c,e−d)
这个时候,我们可以使用数学归纳法大招得到:
g
c
d
(
a
1
,
a
2
,
a
3
,
…
…
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_3,……a_n)=gcd(a_1,a_2-a_1,a_3-a_2……,a_n-a_{n-1})
gcd(a1,a2,a3,……an)=gcd(a1,a2−a1,a3−a2……,an−an−1)
其
实
对
于
n
个
数
的
最
大
公
约
数
,
我
们
可
以
采
用
上
面
的
方
法
,
分
为
前
n
−
1
个
数
和
后
n
−
1
个
数
其实对于n个数的最大公约数,我们可以采用上面的方法,分为前n-1个数和后n-1个数
其实对于n个数的最大公约数,我们可以采用上面的方法,分为前n−1个数和后n−1个数
有了这个式子说明可以通过维护序列的差分同样可以求
g
c
d
gcd
gcd。
利用差分就可以把区间加减变成双点加减。可以用没有
l
a
z
y
lazy
lazy的线段树来维护差分值的
g
c
d
gcd
gcd。
最后用树状数组维护差分,这样就可以
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n)求对应的值.
当我们求解
g
c
d
(
a
l
,
a
l
+
1
,
…
…
a
r
)
gcd(a_l,a_{l+1},……a_r)
gcd(al,al+1,……ar)时,可转化为
g
c
d
(
a
l
,
g
c
d
(
a
l
+
1
−
a
l
,
…
…
,
a
r
−
a
r
−
1
)
)
gcd(a_l,gcd(a_{l+1}-a_l,……,a_r-a_{r-1}))
gcd(al,gcd(al+1−al,……,ar−ar−1)).
悄咪咪地提醒:这题要开 l o n g l o n g long~long long long
下面提供两种建树不同的方法:(貌似方法1快但内存大)
方法1:
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
typedef long long ll;
const int N=1<<19;
template<class o>
inline void qr(o&x) {
char c=g;x=0;int f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=g;}
while(isdigit(c))x=x*10+c-'0',c=g;
x*=f;
}
void write(ll x) {
if(x/10)write(x/10);
putchar(x%10+'0');
}
ll a[N],c[N<<1];
ll gcd(ll a,ll b){return !a?b:gcd(b%a,a);}
void bt(int x,int l,int r) {
if(l==r) { c[x]=a[l]-a[l-1]; return ;}
int mid=(l+r)>>1;
bt(lc,l,mid);
bt(rc,mid+1,r);
c[x]=gcd(c[lc],c[rc]);
}
void change(int x,int l,int r,const int &pos,const ll &d) {
if(l==r) { c[x]+=d; return ;}
int mid=(l+r)>>1;
if(pos<=mid)change(lc,l,mid,pos,d);
else change(rc,mid+1,r,pos,d);
c[x]=gcd(c[lc],c[rc]);
}
ll ans;
void query(int x,int l,int r,const int &L,const int &R) {
if(L<=l&&r<=R)
{ans=gcd(ans,c[x]);return;}
int mid=(l+r)>>1;
if(L<=mid) query(lc,l,mid,L,R);
if(mid< R) query(rc,mid+1,r,L,R);
}
int n,m; ll b[N];
inline void add(int x,ll y) { for( ;x<=n;x+=x&-x)b[x]+=y; }
ll sum(int x) { ll y=0; for( ;x;x&=x-1)y+=b[x]; return y;}
int main() {
qr(n);qr(m);
for(int i=1;i<=n;i++)
qr(a[i]);
bt(1,1,n);
while(m--) {
char s[4];int l,r;ll d;
scanf("%s",s);qr(l);qr(r);
switch(s[0]) {
case 'C':
qr(d);
change(1,1,n,l,d);
if(r<n) d=-d,change(1,1,n,r+1,d),d=-d;
add(l,d);add(r+1,-d);
break;
case 'Q':
ans=abs(sum(l)+a[l]);
if(l<r)query(1,1,n,l+1,r);
write(abs(ans));puts("");
break;
}
}
return 0;
}
方法2:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define g getchar()
using namespace std;
typedef long long ll;
const int N=5e5+10;
struct node{int l,r,lc,rc;ll c;}tr[N*2];int len;
int n,m;
ll a[N],b[N],c[N],d,ans;
ll gcd(ll a,ll b){return !a?b:gcd(b%a,a);}
void bt(int l,int r)
{
int x=++len;
tr[x].l=l;tr[x].r=r;
if(l==r){tr[x].c=b[l];return;}
int mid=(l+r)>>1;
tr[x].lc=len+1;bt(l,mid);
tr[x].rc=len+1;bt(mid+1,r);
tr[x].c=gcd(tr[tr[x].lc].c,tr[tr[x].rc].c);
}
void change(int now,int x)
{
if(tr[now].l==tr[now].r){tr[now].c+=d;return;}
int mid=(tr[now].l+tr[now].r)>>1;
if(x<=mid)change(tr[now].lc,x);
else change(tr[now].rc,x);
tr[now].c=gcd(tr[tr[now].lc].c,tr[tr[now].rc].c);
}
void ask(int now,int l,int r)
{
if(l<=tr[now].l&&tr[now].r<=r){ans=gcd(ans,tr[now].c);return;}
int mid=(tr[now].l+tr[now].r)>>1;
if(l<=mid)ask(tr[now].lc,l,r);
if(mid<r)ask(tr[now].rc,l,r);
}
ll sum(int x)
{
ll y=0;
for( ; x ; x-= x & -x)y+=c[x];
return y;
}
ll add(int x,ll y)
{
for( ;x<=n;x+= x & - x)c[x]+=y;
}
template<class o>
void qr(o&x)
{
char c=g;bool v=(x=0);
while(!( ('0'<=c&&c<='9') || c=='-' ))c=g;
if(c=='-')v=1,c=g;
while('0'<=c&&c<='9')x=x*10+c-'0',c=g;
if(v)x=-x;
}
void write(ll x)
{
if(x/10)write(x/10);
putchar(x%10+'0');
}
int main()
{
freopen("2056.in","r",stdin);
freopen("2056.out","w",stdout);
qr(n);qr(m);
for(int i=1;i<=n;i++)qr(a[i]),b[i]=a[i]-a[i-1];
bt(1,n);
while(m--)
{
char s[2];int l,r;
scanf("%s",s);qr(l);qr(r);
switch(s[0]){
case 'C':
qr(d);
change(1,l);
if(r<n)d=-d,change(1,r+1),d=-d;
add(l,d);add(r+1,-d);
break;
case 'Q':
ans=0;if(l<r)ask(1,l+1,r);
ll al=a[l]+sum(l);
write(abs(gcd(al,ans)));puts("");
break;
}
}
return 0;
}