题目2
题意:
给定n个数。有两种操作,操作1是将某个数置为x,操作2是查询区间[l,r]是否可以通过改变至多一个元素使得区间的gcd是否为x。
1
≤
n
≤
5
⋅
1
0
5
,
1
≤
a
i
≤
1
0
9
,
1
≤
q
≤
4
⋅
1
0
5
,
1
≤
l
≤
r
≤
n
,
1
≤
x
≤
1
0
9
1 ≤ n ≤ 5·10^5,1 ≤ a_i ≤ 10^9,1 ≤ q ≤ 4·10^5,1 ≤ l ≤ r ≤ n, 1 ≤ x ≤ 10^9
1 ≤ n ≤ 5⋅105,1 ≤ ai ≤ 109,1 ≤ q ≤ 4⋅105,1 ≤ l ≤ r ≤ n, 1 ≤ x ≤ 109
分析:
首先这种题目单点修改,gcd的区间查询很容易想到使用线段树来进行维护。现在的问题是如何快速的找到那个需要修改的点。对于我们查找的区间,如果区间的gcd不为x的倍数,则说明这个区间的元素必须要修改至少一个。所以我们直接在线段树的查询时操作即可,将区间一分为二,因为最多只能修改一个,所以这个过程是logn的复杂度。维护一个cnt变量,来判断需要修改多少个元素,超过1时就直接返回。
#include <iostream>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
if( b == 0 ) return a;
return gcd(b,a%b);
}
struct node{
ll l,r,g;
}a[2000005];
ll num[500005],cnt = 0;
void update(int x)
{
a[x].g = gcd(a[2*x].g,a[2*x+1].g);
}
void build( int x,int l,int r )
{
a[x].l = l;
a[x].r = r;
if( l == r )
{
a[x].g = num[l];
return;
}
int mid = l + ( r - l ) / 2;
build(2*x,l,mid);
build(2*x+1,mid+1,r);
update(x);
}
void change(int x,int l,int r,int k)
{
if( a[x].l == l && a[x].r == r )
{
a[x].g = k;
return;
}
int mid = a[x].l + ( a[x].r - a[x].l ) / 2;
if( r <= mid ) change(2*x,l,r,k);
else if( l > mid ) change(2*x+1,l,r,k);
else
{
change(2*x,l,mid,k);
change(2*x+1,mid+1,r,k);
}
update(x);
}
bool query(int x,int l,int r,ll z)
{
if( a[x].g % z == 0 ) return true;
if( a[x].l == a[x].r ) return ++cnt <= 1;
int mid = a[x].l + ( a[x].r - a[x].l ) / 2;
if( r <= mid ) return query(2*x,l,r,z);
else if( l > mid ) return query(2*x+1,l,r,z);
else return query(2*x,l,mid,z) && query(2*x+1,mid+1,r,z);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> num[i];
}
build(1,1,n);
int m;
cin >> m;
for( int i = 0 ; i < m ; i++ )
{
int kind;
cin >> kind;
if( kind == 2 )
{
int id,v;
cin >> id >> v;
change(1,id,id,v);
}else
{
ll l,r,x;
cin >> l >> r >> x;
cnt = 0;
if( query(1,l,r,x) ) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
}
return 0;
}