区间最大公约数

5 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:https://www.acwing.com/problem/content/description/247/

可以用常数小的树状数组来求和,用线段树单点修改2333

这个题需要用到更相减损术

gcd(a,b) == gcd(a,b- a),数学归纳法可以推广gcd(a,b,c) == gcd(a,b - a,c -b)

这样我们可以维护一个b数组等于a[i] - a[i - 1],我们可以开心的发现每次在区间上加上一个d的时候b数组的b[l] += d;

b[r + 1] -= d;所以线段树只用两次单点修改,树状数组维护修改后a数组的值

我们还要考虑一点两个数的公约数我们规定是正数,我们在减的过程中会把数减成负数所以我们最重要返回他的绝对值

#include <bits/stdc++.h>
#define gcd __gcd
#define endl '\n'
using namespace std;

typedef long long ll;
typedef long double ld;

const ll maxn = 5e5 + 7;
const ll mod = 1e9 + 7;

struct node
{
	ll l,r,dat;
}
t[maxn << 2];
ll a[maxn],b[maxn],c[maxn];
ll lowbit(ll x)
{
	return x & -x;
}

void update(ll x,ll y)
{
	for(int i = x; i <= maxn; i += lowbit(i))
	{
		c[i] += y;
	}
}

ll getsum(ll x)
{
	ll ans = 0;
	for(int i = x; i > 0; i -= lowbit(i))
	{
		ans += c[i]; 
	}	
	return ans;
}
void build(ll p,ll l,ll r)
{
	t[p].l = l,t[p].r = r;
	if(l == r){t[p].dat = b[l];return ;}
	ll mid = (l + r) / 2;
	build(p * 2,l,mid);
	build(p * 2 + 1,mid + 1,r);
	t[p].dat = gcd(t[p * 2].dat,t[p * 2 + 1].dat);
}

void change(ll p,ll x,ll y)
{
	if(t[p].l == t[p].r){t[p].dat += y;return ;}
	ll mid = (t[p].l + t[p].r) / 2;
	if(mid >= x)change(p * 2,x,y);
	else change(p * 2 + 1,x,y);
	t[p].dat = gcd(t[p * 2].dat,t[p * 2 + 1].dat);
}

ll ask(ll p,ll l,ll r)
{
	if(t[p].l >= l && t[p].r <= r)return t[p].dat;
	ll mid = (t[p].l + t[p].r) / 2;
	ll gc = 0;
	if(mid >= l)gc = gcd(gc,ask(p * 2,l,r));
	if(mid < r)gc = gcd(gc,ask(p * 2 + 1,l,r));
	return abs(gc);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	
	ll n,m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
		b[i] = a[i] - a[i - 1];
	} 
	build(1,1,n);
	
	char c;
	ll x,y,z;
	for(int i = 1; i <= m; i ++)
	{
		cin >> c;
		if(c == 'Q')
		{
			cin >> x >> y;
			ll be = a[x] + getsum(x);
			ll en = ask(1,x + 1,y);
			cout << gcd(be,en) << endl; 
		}
		else if(c == 'C')
		{
			cin >> x >> y >> z;
			change(1,x,z);
			if(y < n)change(1,y + 1,-z);
			update(x,z);
			update(y + 1,-z); 
		}
	}
	
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我们可以使用线段树来解决这个问题。对于每个区间,我们都可以预处理出其内部所有数的最大公约数,然后在询问时,查询覆盖该询问区间的所有区间最大公约数并取最大值即可。 具体地,我们可以将二维区间 $(i,j)$ 分别看作 $i$ 和 $j$ 两个维度,建立一颗二维线段树。对于每个节点 $(x,y)$,它表示的区间为 $[l_x,r_x]\times[l_y,r_y]$,其中 $l_x,r_x,l_y,r_y$ 分别表示该节点在 $x$ 和 $y$ 维度上的左右边界。我们可以在每个节点上维护一个值 $g_{x,y}$,表示区间 $[l_x,r_x]\times[l_y,r_y]$ 内部所有数的最大公约数。 对于每个节点 $(x,y)$,我们可以通过递归地计算其左右儿子节点的 $g$ 值来求出该节点的 $g$ 值。具体地,我们可以将节点 $(x,y)$ 表示的区间分成四个子区间,分别为 $[l_x,\lfloor\frac{l_x+r_x}{2}\rfloor]\times[l_y,\lfloor\frac{l_y+r_y}{2}\rfloor]$、$[\lfloor\frac{l_x+r_x}{2}\rfloor+1,r_x]\times[l_y,\lfloor\frac{l_y+r_y}{2}\rfloor]$、$[l_x,\lfloor\frac{l_x+r_x}{2}\rfloor]\times[\lfloor\frac{l_y+r_y}{2}\rfloor+1,r_y]$ 和 $[\lfloor\frac{l_x+r_x}{2}\rfloor+1,r_x]\times[\lfloor\frac{l_y+r_y}{2}\rfloor+1,r_y]$。然后我们可以递归地计算出这四个子区间的 $g$ 值,然后将它们合并起来得到该节点的 $g$ 值。合并方法为取四个子区间的 $g$ 值的最大公约数。 查询时,我们从根节点开始,递归地查找覆盖询问区间的节点,并将这些节点的 $g$ 值取最大值。具体地,对于当前节点 $(x,y)$,如果它表示的区间与询问区间不相交,则直接返回 1。否则,如果它表示的区间完全包含询问区间,则返回该节点的 $g$ 值。否则,我们将询问区间分成四个子区间,并递归地查询每个子区间,然后将它们的 $g$ 值取最大公约数作为当前节点的 $g$ 值返回。 时间复杂度为 $O((n+m)\log^2(n+m))$,其中 $n$ 和 $m$ 分别为二维区间的行数和列数。空间复杂度为 $O((n+m)\log^2(n+m))$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值