CH4302 interval GCD(最大公因数/线段树/树状数组)

链接:https://ac.nowcoder.com/acm/contest/1033/B
来源:牛客网

题目描述

给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

2、“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

对于每个询问,输出一个整数表示答案。

输入描述:

第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行表示M条指令,每条指令的格式如题目描述所示。

输出描述:

对于每个询问,输出一个整数表示答案。
每个答案占一行。

示例1

输入

复制

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

输出

复制

1
2
4

注意到多个数gcd的性质:gcd(x, y, z) = gcd(x, y - x, z - y),和区间加减法结合起来就可做了。具体参见蓝书p217.

注意:可能要进行很多次区间加法,因此要开long long。

区间修改的时候if(r < n) 才进行change(1, r + 1 , -d); 否则是没有意义的(因为永远用不到r + 1,但是r + 1却会影响线段树上一层结点的值)

ask的时候要对答案加绝对值(因为对某一个点可能进行多次减法变成负数了)。

#include <iostream>
#define int long long
using namespace std;
int n, m, a[500005], b[500005], c[500005];
int gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;
}
struct SegmentTree
{
	int l, r, dat;
} t[2000005];
void build(int p, int l, int r)
{
	t[p].l = l, t[p].r = r;
	if(l == r) 
	{
		t[p].dat = b[l];
		return;                                                                                
	}
	int mid = (l + r) >> 1;
	build(2 * p, l, mid);
	build(2 * p + 1, mid + 1, r);
	t[p].dat = gcd(t[2 * p].dat, t[2 * p + 1].dat);
}
void change(int p, int x, int v)//需要对差分数组进行单点修改
{
	if(t[p].l == t[p].r)
	{
		t[p].dat += v;
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if(x <= mid) change(2 * p, x, v);
	else change(2 * p + 1, x, v);
	t[p].dat = gcd(t[2 * p].dat, t[2 * p + 1].dat);
}
int ask(int p, int l, int r)
{
	if(t[p].l >= l && t[p].r <= r) 
	{
		return abs(t[p].dat);//注意要绝对值
	}
	int mid = (t[p].l + t[p].r) >> 1;
	int ans = 0x3f3f3f3f;
	if(l <= mid) ans = ask(2 * p, l, r);
	if(r > mid)
	{
		if(ans != 0x3f3f3f3f) ans = gcd(ans, ask(2 * p + 1, l, r));
		else ans = ask(2 * p + 1, l, r);
	}
	return abs(ans);
}
//以下为BIT
int query(int x)//查询的是前缀和
{
	int ans = 0;
	for(; x; x -= x & -x) ans += c[x];
	return ans;
}
void add(int x, int y)
{
	for(; x <= 500005; x += x & -x)
	{
		c[x] += y;
	}
}
signed main()
{	
	//freopen("data.txt", "r", stdin);
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i = 1; i <= n; i++) 
	{
		cin >> a[i];
		b[i] = a[i] - a[i - 1];
	}
	for(int i = 1; i <= n; i++) 
	{
		add(i, b[i]);//树状数组维护的是差分序列 因此这里应该初始化为b[i]?
	}
	build(1, 1, n);
	//cout << ask(1, 2, n) << endl;
	for(int i = 1; i <= m; i++)
	{
		char op; 
		int l, r, d;
		cin >> op;
		if(op == 'Q')
		{
			cin >> l >> r;
			//cout << query(l) << ' ' << ask(1, l + 1, r) << endl;
			if(l < r) cout << gcd(query(l), ask(1, l + 1, r)) << endl;
			else cout << gcd(query(l), 0) << endl;
		}
		else
		{
			cin >> l >> r >> d;
			change(1, l, d);
			if(r < n) change(1, r + 1 , -d);//要判r < n
			add(l, d);
			add(r + 1, -d);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/14447975.html