D. Bash and a Tough Math Puzzle(线段树维护gcd+分治)

题目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 1n5105,1ai109,1q4105,1lrn,1x109

分析:

    首先这种题目单点修改,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;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值