题意:
给你一个序列a,两种操作,操作0是询问一个区间l,r,问是否最多修改一个数使得区间的gcd是x;操作1是单点修改。5e5
题解:
这种可以合并的题一看就是可以线段树维护的。
对于操作0,我们用线段树维护区间gcd,如果区间gcd是x的倍数,那么把任意一个数改成x就可以了,要么就只有一个数不是x的倍数,把那个数改成x就可以了。其他情况都没法通过只改一个点得到。
对于操作1,就正常线段树单点修改就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,a[1000010];
struct node
{
int l,r,x;
}tr[4000010];
inline int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
inline void pushup(int rt)
{
tr[rt].x=gcd(tr[rt<<1].x,tr[rt<<1|1].x);
}
inline void build(int rt,int l,int r)
{
tr[rt].l=l;
tr[rt].r=r;
if(l==r)
{
tr[rt].x=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline int query(int rt,int le,int ri,int x)
{
if(tr[rt].x%x==0)
return 1;
int l=tr[rt].l,r=tr[rt].r;
if(l==r)
return 2;
int mid=(l+r)>>1;
if(le<=mid)
{
int ji1=query(rt<<1,le,ri,x),ji2=1;
if(!ji1)
return 0;
if(mid+1<=ri)
ji2=query(rt<<1|1,le,ri,x);
if((ji1==1&&ji2==1))
return 1;
if((ji1==2&&ji2==1)||(ji2==2&&ji1==1))
return 2;
return 0;
}
if(mid+1<=ri)
{
int ji1=query(rt<<1|1,le,ri,x);
return ji1;
}
}
inline void update(int rt,int x,int y)
{
int l=tr[rt].l,r=tr[rt].r;
if(l==r)
{
tr[rt].x=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
update(rt<<1,x,y);
else
update(rt<<1|1,x,y);
pushup(rt);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
int opt;
scanf("%d",&opt);
if(opt==1)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
if(query(1,l,r,x))
printf("YES\n");
else
printf("NO\n");
}
else
{
int x,y;
scanf("%d%d",&x,&y);
update(1,x,y);
}
}
return 0;
}