zoukankan      html  css  js  c++  java
  • caioj 2056 & 0x40数据结构进阶(0x43 线段树)例题2:区间最大公约数

    传送门

    此题细节很多

    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;
    }
    				
    
  • 相关阅读:
    Cortex-M3 在C中上报入栈的寄存器和各fault状态寄存器
    Cortex-M3 双堆栈指针(MSP&PSP)
    Cortex-M3 异常返回值EXC_RETURN
    Cortex-M3 异常中断响应与返回
    Cortex-M3 操作模式与特权等级
    Cortex-M3 R0~R15寄存器组 & 特殊功能寄存器组
    【Python】批量修改指定目录下所有文件的文件名/后缀
    【EMV L2】EMV终端数据
    【EMV L2】Cardholder Verification Rule(CVR) Format
    【EMV L2】GPO响应以及AIP、AFL
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373905.html
Copyright © 2011-2022 走看看