此题细节很多
1.
在lyd的书里提到的这个性质叫更相减损术,可以推广到多个数的情况。
更相减损术其实是欧几里得算法的一个特例。即。
推广:
再推广一下:
继续不耐烦地推广:
这个时候,我们可以使用数学归纳法大招得到:
有了这个式子说明可以通过维护序列的差分同样可以求。
利用差分就可以把区间加减变成双点加减。可以用没有的线段树来维护差分值的。
最后用树状数组维护差分,这样就可以求对应的值.
当我们求解时,可转化为
.
悄咪咪地提醒:这题要开
下面提供两种建树不同的方法:(貌似方法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;
}