题意
给出一个长度为(n)的数列(a),两种询问,第一种给出三个数(l,r,x),区间([l,r])的(gcd)值是否和(x)相似,若最多改变区间([l,r])中的一个数使区间([l,r])的(gcd)值等于(x),则相似,第二种给出两个数(i,y),将(a[i])变为(y)。
分析
建一个线段树维护区间(gcd),这个线段树非常好写,因为是单点修改,所以不需要tag数组和pushdown,查询的时候用一个变量(cnt),记录区间([l,r])中有多少个数不是(x)的倍数,(cnt>1)时直接跳出。
Code
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const double PI=acos(-1.0);
const double eps=1e-6;
const int inf=1e9;
const int mod=1e9+7;
const int maxn=5e5+10;
int n,a[maxn];
int ans[maxn*5];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void pushup(int p){
ans[p]=gcd(ans[p<<1],ans[p<<1|1]);
}
void build(int l,int r,int p){
if(l==r){
ans[p]=a[l];
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
pushup(p);
}
void update(int dl,int dr,int l,int r,int p,int k){
if(l>=dl&&r<=dr){
ans[p]=k;
return;
}
int mid=l+r>>1;
if(dl<=mid) update(dl,dr,l,mid,p<<1,k);
if(dr>mid) update(dl,dr,mid+1,r,p<<1|1,k);
pushup(p);
}
int cnt;
bool query(int dl,int dr,int l,int r,int p,int x){
if(l>=dl&&r<=dr){
if(ans[p]%x==0) {//这个区间的gcd是x的倍数时直接返回true,否则继续向下查询
return true;
}
if(l==r){//这个数不是x的倍数,cnt++
cnt++;
if(cnt>1) return false;
return true;
}
}
int mid=l+r>>1;
if(dl<=mid) if(!query(dl,dr,l,mid,p<<1,x)) return false;
if(dr>mid) if(!query(dl,dr,mid+1,r,p<<1|1,x)) return false;
return true;
}
int main(){
//ios::sync_with_stdio(false);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,n,1);
int q;
scanf("%d",&q);
while(q--){
int op,l,r,x,i,y;
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&l,&r,&x);
cnt=0;
if(query(l,r,1,n,1,x)){
puts("YES");
}else{
puts("NO");
}
}else{
scanf("%d%d",&i,&y);
update(i,i,1,n,1,y);
}
}
return 0;
}