http://codeforces.com/problemset/problem/914/D
题意:给出一个数组,现有两种操作
1.给出l,r,x 询问在l,r区间内至多改变一个数,gcd能否为x
2.单点修改元素值
思路:考虑线段树维护,最麻烦的是改变一个元素后gcd能否为x,首先线段树维护每个区间的gcd,首先父节点储存的gcd如果能都整除x,那么该区间gcd就可以在改变一个数的情况下为x
比如 x=2 查询区间数值为 4 8 12 4,任意改变一个数为2即符合题意。
如果该父亲区间不能整除x,那么继续往下寻找到叶子节点,找到不能整除x的叶子节点,假设将其改变为x,并且记录数目,因为我们只能改一个,那么如果大于一个直接不合法,所以我们最多找到2个不满足条件的数递归结束,所以时间复杂度可以满足要求
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define fi first
#define se second
#define show(a) cout<<"Here is "<<a<<endl;
#define show2(a,b) cout<<"Here is "<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<"Here is "<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
typedef pair<P, int> LP;
const ll inf = 1e17 + 10;
const int N = 3e6 + 10;
const ll mod = 10007;
const int base=131;
int n,block,m,id,t,x;
int bl[N],cnt;
ll a[N],k;
ll ans,res[N];
ll tree[N];
//unordered_map<int,int> mp;
tr1::unordered_map<ll,int> num;
void pushup(int rt)
{
tree[rt]=__gcd(tree[rt<<1],tree[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
cin>>tree[rt];
return ;
}
int m=l+r >> 1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
bool query(int L,int R,int x,int l,int r,int rt)
{
if(tree[rt]%x==0) return 1;
if(l==r) return --cnt>=0;
int m=l+r >> 1;
if(R<=m) return query(L,R,x,l,m,rt<<1);
else if(L>=m+1) return query(L,R,x,m+1,r,rt<<1|1);
else return (query(L,R,x,l,m,rt<<1)&&query(L,R,x,m+1,r,rt<<1|1));
}
void update(int pos,int x,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=x;
return ;
}
int m=l+r >> 1;
if(pos<=m) update(pos,x,l,m,rt<<1);
else update(pos,x,m+1,r,rt<<1|1);
pushup(rt);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
build(1,n,1);
cin>>t;
int l,r;
while(t--)
{
cin>>k;
if(k==1)
{
cnt=1;
cin>>l>>r>>x;
if(query(l,r,x,1,n,1)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
else
{
cin>>id>>x;
update(id,x,1,n,1);
}
}
}