题目:http://codeforces.com/contest/914/problem/D
题意:有查询和更新两个操作,更新是直接把某个值更新为给定值,查询是查询给定区间上的最大公约数(GCD),如果最大公约数是题中所给值(x)或者通过更改区间上的一个值可以使该区间的GCD变成x,则输出YES,不然输出NO。
题解:用线段树维护区间上的GCD,更新就是单点更新,而查询操作emm...看代码注释吧
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls id*2
#define rs id*2+1
#define mid (tree[id].l+tree[id].r)/2
const int maxn=1e6+1e5+10;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
struct node{
ll l,r,v;
}tree[maxn];
ll cnt=0,n;
void pushup(ll id){
tree[id].v=gcd(tree[ls].v,tree[rs].v);
}
void build(ll id,ll L,ll R){
tree[id].l=L;
tree[id].r=R;
if(L==R){
scanf("%I64d",&tree[id].v);
return;
}
build(ls,L,mid);
build(rs,mid+1,R);
pushup(id);
}
void update(ll i,ll y,ll id){//单点更新
if(tree[id].l==tree[id].r){
tree[id].v=y;
return;
}
if(i<=mid) update(i,y,ls);
else update(i,y,rs);
pushup(id);
}
void query(ll id,ll L,ll R,ll x){//查询
if(cnt>=2) return;
//计数要更改的值的个数,超过2时直接返回,输出结果为NO
if(tree[id].l==tree[id].r){
//更新到叶子节点,如果这个值需要更改的话,cnt++
if(tree[id].v%x!=0){
cnt++;
}
return;
}
if(L<=tree[id].l&&R>=tree[id].r){
//对于包含在查询区间里的线段,如果这一段的值不等于x,则说明这个线段里存在需要更改的值
if(tree[id].v!=x){
//直接暴力更新到叶子节点会T ,所以分左右来更新
//因为这一段的GCD已经不等于x,则其左右子节点至少有一个不能被x整除 (注意不是等于x )
if(tree[ls].v%x==0) query(rs,L,R,x); //左边能整除说明要更改的值一定在右边,下同理
else if(tree[rs].v%x==0) query(ls,L,R,x)
else cnt=2;//两边都不能说明至少有两个值需要更新,输出NO
}
return;
}
if(L<=mid) query(ls,L,R,x);
if(R>mid) query(rs,L,R,x);
}
int main(){
scanf("%I64d",&n);
build(1,1,n);
ll m,b,c,d,e;
scanf("%I64d",&m);
while(m--){
scanf("%I64d%I64d%I64d",&b,&c,&d);
if(b==1){
scanf("%I64d",&e);
cnt=0;
query(1,c,d,e);
// cout<<cnt<<endl;
if(cnt<=1) printf("YES\n");
else printf("NO\n");
}
else{
update(c,d,1);
}
}
return 0;
}