AcWing 246 区间最大公约数 (线段树)

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

更相减损术:\(gcd(a,b) = gcd(a,a - b)\)
可以扩展到 \(n\) 个数,\(gcd(a,b,c) = gcd(a,b-a,c-b)\)

于是可以用线段树维护\(a\)的差分数组\(b\),查询时的答案即为\(gcd(a[L],query(l+1,r)\)

细节:

  1. \(gcd(a,b) = gcd(a,-b)\), 对结果取负
  2. \(gcd\)\(a,b\)\(0\)没有关系
  3. \(long long\)
  4. \(r+1\)可能越界,\(n+1\) 即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 500010;

int n, m;
ll a[maxn], b[maxn], c[maxn];

char s[10];

struct SEG{
	ll gc;
}t[maxn << 2];

ll gcd(ll x, ll y){ return y? gcd(y, x % y) : x; }

void add(ll x, ll y){
	for(; x <=n ; x += x & (-x))
		c[x] += y;
}

ll ask(ll x){
	ll sum = 0;
	for(; x ; x -= x & (-x))
		sum += c[x];
	return sum;
}

void pushup(int i){
	t[i].gc = gcd(t[i << 1].gc, t[i << 1 | 1].gc); 
} 

void build(int i, int l, int r){
	if(l == r){
		t[i].gc = b[l];
		return;
	}
	
	int mid = (l + r) >> 1;
	build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); 
	pushup(i);
}

void modify(int i, int l, int r, int p, ll k){
	if(l == r){
		t[i].gc += k;
		return;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) modify(i << 1, l, mid , p, k);
	else modify(i << 1 | 1, mid + 1, r, p ,k);
	pushup(i);
}

ll query(int i, int l, int r, int x, int y){
	if(x > y) return 0;
	if(x <= l && r <= y){
		return t[i].gc;
	}
	int mid = (l + r) >> 1;

	if(y <= mid) return query(i << 1, l, mid, x, y);
	else if(x > mid) return query(i << 1 | 1, mid + 1 , r, x, y);
	else return gcd(query(i << 1, l, mid, x, y), query(i << 1 | 1, mid + 1 , r, x, y));
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	scanf("%d%d", &n, &m);
	
	for(int i = 1; i <= n; ++i){
		scanf("%lld",&a[i]);
		b[i] = a[i] - a[i - 1];
		add(i, b[i]);
	}
	b[++n] = -b[n-1];
	
	build(1, 1, n);
 	
 	int x, y; ll d;
 	for(int i = 1; i <= m; ++i){
 		scanf("%s%d%d", s, &x, &y);
 		if(s[0] == 'Q'){
 			ll A = ask(x);
 			ll B = query(1, 1, n, x + 1, y);
 			printf("%lld\n", abs(gcd(A, B)));
		} else{
			scanf("%lld", &d);
			modify(1, 1, n, x, d);
			modify(1, 1, n, y + 1, -d);
			add(x, d); add(y + 1, -d); 
		}
	}
 	
	return 0;
}
posted @ 2020-11-11 12:35  Tartarus_li  阅读(104)  评论(0编辑  收藏  举报