• 小阳的贝壳(区间GCD)


    小阳的贝壳(区间GCD)

    题目链接:https://ac.nowcoder.com/acm/problem/26255

    题目大意:区间修改,求区间(gcd),区间相邻两值之间差值最大值。

    分析:

    首先是区间相邻两值之间差值最大值,这个比较好求不作讨论

    求区间(gcd)的,很显然首先想到线段树,维护序列,区间查询,可惜这里是区间修改,直接维护序列a显然是不行的。以为在区间里无法得知修改后的(gcd),并(gcd)也无法通过懒标记下放给子节点(反正我是不知道)

    由某些数学公式可得: (gcd(a,b)=gcd (a,b-a)))(貌似是九章算术里面的吧),这样就能推出 (gcd(a,b,c)= gcd(a,b-a,c-b))

    得到这个公式后,观察可发现我们把维护的序列a,更改为维护序列a的差分序列b,因为维护差分序列b,就实现了对线段树的单点修改,也就不必考虑修改后区间的(gcd)(gcd)懒标记下放。

    代码说明:

    线段树维护区间(sum),区间(gcd),区间(max),其中(sum)的序列为(a,gcd)(max)的序列为(b)

    我们对(b)序列区间修改只需要(b[ql]+x,b[qr+1]-x)

    求解区间(gcd(ql,qr))(gcd(sum[ql],b[ql+1...qr]))

    注意:

    注意绝对值问题

    代码:
    #include <iostream>
    #define lch 2*k
    #define rch 2*k+1
    #define mid (l+r)/2
    using namespace std;
    typedef long long ll;
    const int N=1e5+7;
    int n,m;
    ll a[N],pre[N];
    ll tree[4*N],god[4*N],maxn[4*N],tap[4*N]; // 区间sum,区间gcd,区间差值max,区间sum懒标记
    ll gcd(ll x,ll y){
    	if(x==0&&y==0) return 0;
    	return y==0? x: gcd(y,x%y);
    }
    
    void init(int k,int l,int r){
    	tap[k]=0;
    	if(l>=r){
    		tree[k]=a[l];
    		god[k]=pre[l];
    		maxn[k]=pre[l];
    		return;
    	}
    	init(lch,l,mid);
    	init(rch,mid+1,r);
    	tree[k]=tree[lch]+tree[rch];
    	maxn[k]=max(abs(maxn[lch]),abs(maxn[rch]));
    	god[k]=gcd(abs(god[lch]),abs(god[rch]));
    }
    void pushdown(int k,int zlen,int ylen){
    	if(tap[k]){
    		tree[lch]+=tap[k]*zlen;
    		tree[rch]+=tap[k]*ylen;
    		tap[lch]+=tap[k];
    		tap[rch]+=tap[k];
    		tap[k]=0;
    	}
    }
    void updateT(int k,int l,int r,int ql,int qr,ll cost){
    	if(ql<=l&&r<=qr){
    		tree[k]+=cost*(r-l+1);
    		tap[k]+=cost;
    		return;
    	}
    	pushdown(k,mid-l+1,r-mid);
    	if(ql<=mid) updateT(lch,l,mid,ql,qr,cost);
    	if(mid+1<=qr) updateT(rch,mid+1,r,ql,qr,cost);
    	tree[k]=tree[lch]+tree[rch];
    }
    
    ll qryT(int k,int l,int r,int ql,int qr){
    	if(ql<=l&&r<=qr){
    		return tree[k];
    	}
    	int sum1=0,sum2=0;
    	pushdown(k,mid-l+1,r-mid);
    	if(ql<=mid) sum1=qryT(lch,l,mid,ql,qr);
    	if(mid+1<=qr) sum2=qryT(rch,mid+1,r,ql,qr);
    	return sum1+sum2;
    }
    
    
    
    void updateG(int k,int l,int r,int ql,int qr,ll cost){
    	if(ql>n) return;
    	if(ql<=l&&r<=qr){
    		god[k]+=cost;
    		maxn[k]+=cost;
    		return ;
    	}
    	if(ql<=mid) updateG(lch,l,mid,ql,qr,cost);
    	if(mid+1<=qr) updateG(rch,mid+1,r,ql,qr,cost);
    	//注意取abs
    	god[k]=gcd(abs(god[lch]),abs(god[rch]));
    	maxn[k]=max(abs(maxn[lch]),abs(maxn[rch]));
    }
    
    ll qryG(int k,int l,int r,int ql,int qr){
    	if(ql>qr){
    		return 0;
    	}
    	if(ql<=l&&r<=qr){
    		return god[k];
    	}
    	int gcd1=0,gcd2=0;
    	if(ql<=mid) gcd1=qryG(lch,l,mid,ql,qr);
    	if(mid+1<=qr) gcd2=qryG(rch,mid+1,r,ql,qr);
    	//注意取abs
    	return gcd(abs(gcd1),abs(gcd2));
    }
    
    ll qryM(int k,int l,int r,int ql,int qr){
    	if(ql>qr) return 0;
    	if(ql<=l&&r<=qr){
    		return maxn[k];
    	}
    	int max1=0,max2=0;
    	if(ql<=mid)	max1=qryM(lch,l,mid,ql,qr);
    	if(mid+1<=qr) max2=qryM(rch,mid+1,r,ql,qr);
    	//注意取abs
    	return max(abs(max1),abs(max2));
    }
    
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++){
    		cin>>a[i];
    		pre[i]=a[i]-a[i-1];
    	}
    	init(1,1,n);
    	while(m--){
    		int op,ql,qr,x;
    		scanf("%d %d %d",&op,&ql,&qr);
    		if(op==1){
    			scanf("%d",&x);
    			updateT(1,1,n,ql,qr,x);  //序列a区间修改
    			updateG(1,1,n,ql,ql,x);  //差分b区间修改
     			updateG(1,1,n,qr+1,qr+1,-x);
    		}else if(op==2){
    			printf("%lld
    ",qryM(1,1,n,ql+1,qr));
    		}else{
    			//由公式ql到qr的gcd为ql的值与ql到qr之间的差分的gcd的gcd
    			//注意为ql+1
    			printf("%lld
    ",gcd(qryT(1,1,n,ql,ql),qryG(1,1,n,ql+1,qr)));
    		}	
    	}
    }
    
  • 相关阅读:
    Linux内核info leak漏洞
    ELK Stack部署
    centos下安装opencv
    windows10 进入BIOS
    Dockerfile语法简介
    JAVA 容器配置 JVM 监控
    docker registry
    squid
    正反向代理
    安装plsql
  • 原文地址:https://www.cnblogs.com/kksk/p/14276500.html
Copyright © 2020-2023  润新知