gcd,辗转相减法,线段树,246. 区间最大公约数
246. 区间最大公约数
给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:
C l r d
,表示把 A[l],A[l+1],… 都加上 d。Q l r
,表示询问 A[l],A[l+1],… 的最大公约数(GCD)。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数 N,M。
第二行 N 个整数 A[i]。
接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
N≤500000,M≤100000,
1≤A[i]≤10^18,
|d|≤10^18,
保证数据在计算过程中不会超过 long long 范围。
输入样例:
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
解析:
根据lyd的《算法进阶指南》:
通过辗转相减法可知:gcd(x,y)=gcd(x,y-x)。可以进一步拓展为三个数gcd(a,b,c)=gcd(a,b-a,c-b);
以此类推:询问“Q,l, r,就相当于求gcd(A[l],ask(1,l+1,r));
注意特殊情况:
if (l < r) {
printf("%lld\n", gcd(t, ask(1, l + 1, r)));
}
else {
printf("%lld\n", t);
}
否则ask(1,l+1,r),当l+1>r时会出错;
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5;
struct node {
int l, r;
LL dat,v;
}t[4*N];
int n, m;
LL arr[N];
LL gcd(LL a, LL b) {
a = a < 0 ? -a : a;
b = b < 0 ? -b : b;
if (b == 0) {
return a;
}
return gcd(b,a%b);
//return __gcd(a, b);
}
void build(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l == r) {
t[p].dat = arr[l] - arr[l - 1];
t[p].v = arr[l] - arr[l - 1];
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);
t[p].v = t[p * 2].v+ t[p * 2 + 1].v;
}
void change(int p, int y, LL x) {
if (t[p].l == t[p].r) {
t[p].dat += x;
t[p].v += x;
return;
}
LL mid = (t[p].l + t[p].r) / 2;
if (y <= mid)change(p * 2, y, x);
if (y > mid)change(p * 2 + 1, y, x);
t[p].dat = gcd(t[p * 2].dat, t[p * 2 + 1].dat);
t[p].v = t[p * 2].v+ t[p * 2 + 1].v;
}
LL ask(int p,int l,int r) {
if (l <= t[p].l && r >= t[p].r) {
return t[p].dat;
}
LL mid = (t[p].l + t[p].r) / 2;
if (l <= mid && r > mid) {
return gcd(ask(p * 2, l, mid), ask(p * 2 + 1, l, r));
}
else if (l <= mid) return ask(p * 2, l, r);
return ask(p * 2 + 1, l, r);
/*if (r <= mid) return ask(p * 2, l, r);
else if (l > mid)return ask(p * 2 + 1, l, r);
else gcd(ask(p * 2, l, r), ask(p * 2 + 1, l, r));*/
}
LL ask1(int p,int l,int r) {
if (l <= t[p].l && r >= t[p].r) {
return t[p].v;
}
LL mid = (t[p].l + t[p].r) / 2;
LL val = 0;
if (l <= mid)val += ask1(p * 2, l, r);
if (r > mid) val += ask1(p * 2 + 1, l, r);
return val;
}
int main() {
scanf("%d%d", &n,&m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &arr[i]);
}
build(1, 1, n);
char op[2];
int l, r;
LL d;
while (m--) {
scanf("%s%d%d", op, &l, &r);
if (op[0] == 'C') {
scanf("%lld", &d);
change(1, l, d);
if(r+1<=n)change(1, r + 1, -d);
}
else {
LL t = ask1(1, 1, l);
if (l < r) {
printf("%lld\n", gcd(t, ask(1, l + 1, r)));
}
else {
printf("%lld\n", t);
}
}
}
return 0;
}