给定序列,单点修改,区间询问[l,r]中,能否最多修改一个数使得区间GCD等于x。
(询问时的修改只是想象中的修改,不会改变序列的元素)
线段树维护区间gcd这个很显然了,问题是询问如何处理呢?
刚开始的方向是找满足条件的序列,有什么规律性质,但是在纸上画了很久也没有结果。可以说方向完全错了。
正确方向是暴搜,一个数如果是所询问的x的倍数,那么就等价于这个数等于x的,因为我们可以把最终需要改的那个数修改成x, 所以我们可以直接dfs找不是x倍数的叶子节点,找到两个就不用继续了,此外如果递归到某个区间的GCD已经是x的倍数,也得剪枝剪掉。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
//数列 单点修改 区间查询[l,r]中能否最多修改1个元素 使得gcd为x
struct node
{
#define lc rt<<1
#define rc rt<<1|1
int l,r;
int g;
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt].g=__gcd(tree[lc].g,tree[rc].g);
}
inline void build(int rt,int l,int r)
{
tree[rt].l=l,tree[rt].r=r;
if(l==r)
{
scanf("%d",&tree[rt].g);
return ;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(rt);
}
inline void upd(int rt,int pos,int v)
{
int l=tree[rt].l,r=tree[rt].r;
if(l==r)
{
tree[rt].g=v;
return ;
}
int mid=l+r>>1;
if(pos<=mid) upd(lc,pos,v);
else upd(rc,pos,v);
pushup(rt);
}
int dfs(int rt,int vl,int vr,int v)
{
//区间内都是v的倍数 直接返回0 因为我们修改到1处为x 这个区间就都成x了
int l=tree[rt].l,r=tree[rt].r;
if(r<vl || l>vr) return 0;
if(vl<=l && r<=vr)
{
if(tree[rt].g % v == 0) return 0;//剪枝 这个区间gcd为v
else if(l==r)
{
return 1;
}
}
int mid=l+r>>1;
int ret=dfs(lc,vl,vr,v);
if(ret>=2) return 2;
return ret+dfs(rc,vl,vr,v);
}
int n,q;
int main()
{
scanf("%d",&n);
build(1,1,n);
scanf("%d",&q);
while(q--)
{
int f,x,y;
scanf("%d %d %d",&f,&x,&y);
if(f==1)
{
int v;
scanf("%d",&v);
int cnt=dfs(1,x,y,v);//[x,y]区间内有多少个叶子节点的gcd不为v
if(cnt>=2) puts("NO");
else puts("YES");
}
else
{
upd(1,x,y);
}
}
return 0;
}