zsyzlzy

导航

 

传送门

此题细节很多

1. gcd(a,b)=gcd(a,ba)gcd(a,b)=gcd(a,b−a)

在lyd的书里提到的这个性质叫更相减损术,可以推广到多个数的情况。

更相减损术其实是欧几里得算法的一个特例。即gcd(anb,b)=gcd(a,b)gcd(a−nb,b)=gcd(a,b)

推广:

(a,b,c)=((a,b),(b,c))=((a,ba),(b,cb))=(a,ba,b,cb)(a,b,c)=((a,b),(b,c))=((a,b−a),(b,c−b))=(a,b-a,b,c-b)
(ba,b)=(a,ba)(a,b,c)=(a,ba,cb)由于(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,ba,cb),(b,cb,dc))=(a,b,c,d)=((a,b,c),(b,c,d))=((a,b-a,c-b),(b,c-b,d-c))=
(a,ba,cb,b,cb,dc)=(,)(a,ba,cb,dc)(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,ba,cb,dc),(b,cb,dc,ed))=(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,ba,cb,dc,ed)(去重,化简)(a,b-a,c-b,d-c,e-d)

这个时候,我们可以使用数学归纳法大招得到:

gcd(a1,a2,a3,an)=gcd(a1,a2a1,a3a2,anan1)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})
n,,n1n1其实对于n个数的最大公约数,我们可以采用上面的方法,分为前n-1个数和后n-1个数
有了这个式子说明可以通过维护序列的差分同样可以求gcdgcd
利用差分就可以把区间加减变成双点加减。可以用没有lazylazy的线段树来维护差分值的gcdgcd
最后用树状数组维护差分,这样就可以O(log2n)O(log_2n)求对应的值.
当我们求解gcd(al,al+1,ar)gcd(a_l,a_{l+1},……a_r)时,可转化为
gcd(al,gcd(al+1al,,arar1))gcd(a_l,gcd(a_{l+1}-a_l,……,a_r-a_{r-1})).

悄咪咪地提醒:这题要开long longlong~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;
}
				
posted on 2019-07-22 11:22  zsyzlzy  阅读(159)  评论(0编辑  收藏  举报