codeforces 914D(线段树区间GCD)

题意:q次询问,每次两个操作,1.问你区间【l,r】内修改一个数字后,区间的gcd是否是X,2.单点修改区间【l,r】的值

题解:线段树处理区间的gcd

  • 如果给定的区间【l,r】 在左子树,就在左子树找
  • 否则在右子树找
  • 在否则需要拆区间,这时再来个查询,查询拆区间后的左右子树的gcd是否能整除X,因为区间gcd一定是最小的内个数
  • 如果拆区间后的左右子树一边能整除则继续往下搜
  • 如果拆区间后的左右子树两边都能整除,那就可以返回了,此时修改后gcd一定能是X;
  • 如果拆区间后的左右子树两边都不能整除,那也可以返回了,只是此时修改后gcd一定不是X;
  • 由于查询套查询所以是loglog级别,也可以接受,但可以是一个log的,通过拆要查的区间就好了。
  • 	int lgcd=find_gcd(l,mid,L,mid,p<<1);
    	int rgcd=find_gcd(mid+1,r,mid+1,R,p<<1|1);
    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 1e6 + 5;
    const int INF = 0x3f3f3f3f;
    const ll MOD = 1e9 + 7;
    int gg[maxn*4];
    int a[maxn]; 
    void build(int l,int r,int p){
    	if(l==r) {
    		gg[p]=a[l]; return ;
    	}
    	int mid=(l+r)/2;
    	build(l,mid,p<<1);
    	build(mid+1,r,p<<1|1);
    	gg[p]=__gcd(gg[p<<1],gg[p<<1|1]);
    }
    void update(int l,int r,int p,int pos,int v){
    	if(l==r) {
    		gg[p]=v; return ;
    	}
    	int mid=(l+r)/2;
    	if(pos<=mid) update(l,mid,p<<1,pos,v);
    	else  update(mid+1,r,p<<1|1,pos,v);
    	gg[p]=__gcd(gg[p<<1],gg[p<<1|1]);
    }
    int find_gcd(int l,int r,int L,int R,int p){
    	if(L<=l&&r<=R){
    		return gg[p];
    	}
    	int mid=(l+r)/2;
    	int res=0;
    	if(L<=mid) res=__gcd(res,find_gcd(l,mid,L,R,p<<1));
    	if(R>mid)  res=__gcd(res,find_gcd(mid+1,r,L,R,p<<1|1));
    	return res;
    }
    int query(int l,int r,int L,int R,int p,int x){
    	if(l==r) return 1;
    	int mid=(l+r)/2;
    	int res=0;
    	if(R<=mid) res=query(l,mid,L,R,p<<1,x);//直接进左树找gcd 
    	else if(L>mid) res=query(mid+1,r,L,R,p<<1|1,x);//右树找gcd 
    	else{//得拆区间 
    		int lgcd=find_gcd(l,mid,L,mid,p<<1);
    		int rgcd=find_gcd(mid+1,r,mid+1,R,p<<1|1);
    		if(lgcd%x!=0&&rgcd%x!=0) return 0;//如果拆完的两边都%==0,则一定改变一个数可以使最大公约数变为x  (lgcd/rgcd 都取决与他区间里最小的数) 
    		if(!(lgcd%x)&&!(rgcd%x)) return 1;//负责 直接不可能 
    		if(lgcd%x==0&&rgcd%x!=0) res=query(mid+1,r,L,R,p<<1|1,x);//如果一边区间满足继续搜 
    		if(lgcd%x!=0&&rgcd%x==0) res=query(l,mid,L,R,p<<1,x);
    	}	
    	return res;
    }
    int main(){
    	std::ios::sync_with_stdio(false);
    	int n; cin>>n;
    	for(int i=1;i<=n;i++)  cin>>a[i];
    	int q; cin>>q;
    	build(1,n,1);
        while(q--){
        	int k; cin>>k;
        	if(k==1){
        		int l,r,x; cin>>l>>r>>x;
        		int flag=query(1,n,l,r,1,x);
        		if(flag) cout<<"YES"<<endl;
        		else cout<<"NO"<<endl;
    		}else{
    			int i,y; cin>>i>>y;
    			update(1,n,1,i,y);
    		}
    	}	
    	return 0;
    } 

     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值