【题解】AcWing 246.区间最大公约数

AcWing 246.区间最大公约数

题目描述

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

  1. C l r d,表示把 A [ l ] , A [ l + 1 ] , … , A [ r ] A[l],A[l+1],…,A[r] A[l],A[l+1],,A[r] 都加上 d d d
  2. Q l r,表示询问 A [ l ] , A [ l + 1 ] , … , A [ r ] A[l],A[l+1],…,A[r] A[l],A[l+1],,A[r] 的最大公约数(GCD)。

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

输入格式

第一行两个整数 N , M N,M N,M

第二行 N N N 个整数 A [ i ] A[i] A[i]

接下来 M M M 行表示 M M M 条指令,每条指令的格式如题目描述所示。

输出格式

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

数据范围

N ≤ 500000 , M ≤ 100000 N≤500000,M≤100000 N500000,M100000,
1 ≤ A [ i ] ≤ 1 0 18 1≤A[i]≤10^{18} 1A[i]1018,
∣ d ∣ ≤ 1 0 18 |d|≤10^{18} d1018

输入样例

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

题目分析

根据《九章算术》中的“更相减损术”,我们知道 g c d ( a , b ) = g c d ( a , a − b ) gcd(a,b)=gcd(a,a-b) gcd(a,b)=gcd(a,ab)。再根据数学归纳法,我们可以得到更一般的结论: g c d ( a 1 , a 2 , … , a n ) = g c d ( a 1 , a 2 − a 1 , … , a n − a n − 1 ) gcd(a_1,a_2,…,a_n)=gcd(a_1,a_2-a_1,…,a_n-a_{n-1}) gcd(a1,a2,,an)=gcd(a1,a2a1,,anan1)

不难发现,这里的 a 2 − a 1 , … , a n − a n − 1 a_2-a_1,…,a_n-a_{n-1} a2a1,,anan1 是差分的形式。因此,我们可以构造原数组的差分数组 b b b,用线段树维护数组 b b b 的最大公约数。这样一来,区间修改变成了两次单点修改,区间查询也变成了 g c d ( a [ l ] , q u e r y ( 1 , l + 1 , r ) ) gcd(a[l],query(1,l+1,r)) gcd(a[l],query(1,l+1,r))

对于差分数组的维护,既可用树状数组,也可线段树直接维护。这里直接用线段树维护区间和从而维护差分。

代码:

#include <iostream>
#include <cmath>
using namespace std;

const int N = 500010;
typedef long long ll;

ll a[N], n, m;

struct node{
    
    
	int l, r;
	ll sum, g;  //维护区间和与区间gcd 
}tr[N * 4]; 

ll gcd(ll a, ll b){
    
    
	return b ? gcd(b, a % b) : a;
}

void pushup(node &u, node &l, node &r){
    
    
	u.sum = l.sum + r.sum;
	u.g = gcd(l.g, r.g); 
}

void pushup(int u){
    
    
	pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r){
    
    
	if (l == r){
    
    
		ll b = a[r] - a[r - 1];
		tr[u] = {
    
    l, r, b, b};
	}
	else{
    
    
	    tr[u].l = l, tr[u].r = r;
		int mid = l + r >> 1;
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
} 

void modify(int u, int x, ll d){
    
    
	if (tr[u].l == x && tr[u].r == x){
    
    
		ll b = tr[u].sum + d;
		tr[u] = {
    
    x, x, b, b};
	}
	else{
    
    
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid) modify(u << 1, x, d);
		else modify(u << 1 | 1, x, d);
		pushup(u);
	}
}

node query(int u, int l, int r){
    
    
	if (tr[u].l >= l && tr[u].r <= r) return tr[u];
	else{
    
    
		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid) return query(u << 1, l, r);
		else if (l > mid) return query(u << 1 | 1, l, r);
		else{
    
    
			node left = query(u << 1, l, r), right = query(u << 1 | 1, l, r);
			node res;
			pushup(res, left, right);
			return res;
		}
	}
}

int main(){
    
    
	cin >> n >> m;
	for (int i = 1; i <= n; i ++)
		cin >> a[i];
	
	build(1, 1, n);	
	
	while (m --){
    
    
		char opt[2];
		int l, r;
		ll d;
		cin >> opt >> l >> r;
		if (* opt == 'C'){
    
    
			cin >> d;
			modify(1, l, d);
			if (r + 1 <= n) modify(1, r + 1, -d);
		}
		else{
    
    
			node left = query(1, 1, l);
			node right;
			if (l + 1 <= r) right = query(1, l + 1, r);
			cout << abs(gcd(left.sum, right.g)) << endl;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/f4u4u4r/article/details/117850994