直接给中文题意吧:
给定长度为n的数组a1,a2,…,an。
q次操作。操作分为两种:
1 l r x
,若能在a[l]~a[r]中至多修改一个数的情况下,使得gcd(a[l],a[l+1],…,a[r])=x,输出YES,否则输出NO。注意:我们不需要进行实际的改动。2 i y
,将a[i]修改为y。
Input
第一行为一个整数n(1 ≤ n ≤ 5*105)。
第二行为n个整数,表示数组a(1 ≤ ai ≤ 109)。
第三行为一个整数q(1 ≤ q ≤ 4*105)。
接下来q行,每行一个操作。
对于第一类操作,1 ≤ l ≤ r ≤ n, 1 ≤ x ≤ 109。
对于第二类操作,1 ≤ i ≤ n,1 ≤ y ≤ 109。
Output
对于每个第一类操作,若可行,输出'YES',否则输出'NO'。
输入:
5
1 2 3 4 5
6
1 1 4 2
2 3 6
1 1 4 2
1 1 5 2
2 5 10
1 1 5 2
输出:
NO
YES
NO
YES
分析:通过题意我们容易想到用线段树维护最大公约数,但关键是如何判断至多修改一个点的情况下,能不能使得区间最大公约数等于x,其实就是转化为判断区间中有几个数不是x的倍数,如果有两个及两个以上的数不是x的倍数,那么输出NO,否则输出YES,注意本题需要剪枝,防止区间中存在很多个数都不是x的倍数,那么我们每一个数都要遍历到叶子节点,那么必TLE,其他就没什么了,下面上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=3e6+10;
int g[N],a[N],l[N],r[N];
int ans;
void pushup(int id)
{
g[id]=__gcd(g[id<<1],g[id<<1|1]);
}
void build(int id,int L,int R)
{
l[id]=L;r[id]=R;g[id]=0;
if(L==R)
{
g[id]=a[L];
return ;
}
int mid=L+R>>1;
build(id<<1,L,mid);
build(id<<1|1,mid+1,R);
pushup(id);
}
void update_point(int id,int x,int val)
{
if(l[id]==r[id])
{
g[id]=val;
return ;
}
int mid=l[id]+r[id]>>1;
if(x<=mid) update_point(id<<1,x,val);
else update_point(id<<1|1,x,val);
pushup(id);
}
void query_interval(int id,int L,int R,int x)
{
if(ans>1) return ;//剪枝,否则会TLE
if(l[id]>R||r[id]<L) return ;
if(l[id]==r[id])
{
ans+=(g[id]%x!=0);
return ;
}
if(l[id]>=L&&r[id]<=R)
{
if(g[id]%x==0) return ;//如果整个区间的最大公约数能够被x整除,那么这个区间的所有数都是x的倍数
}
query_interval(id<<1,L,R,x);
query_interval(id<<1|1,L,R,x);
}
int main()
{
int n,q;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
cin>>q;
int ll,rr,x,op;
while(q--)
{
scanf("%d",&op);
if(op==1)
{
ans=0;//不要忘记每次清空ans
scanf("%d%d%d",&ll,&rr,&x);
query_interval(1,ll,rr,x);
if(ans>1) puts("NO");
else puts("YES");
}
else
{
scanf("%d%d",&ll,&x);
update_point(1,ll,x);
}
}
return 0;
}