区间最大公约数
给定一个长度为 N N N 的数列 A A A,以及 M M M 条指令,每条指令可能是以下两种之一:
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。Q l r
,表示询问 A [ l ] , A [ l + 1 ] , … , A [ r ] A[l],A[l+1],…,A[r] A[l],A[l+1],…,A[r] 的最大公约数 ( G C D ) (GCD) (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
N≤500000,M≤100000,
1
≤
A
[
i
]
≤
1
0
18
1≤A[i]≤10^{18}
1≤A[i]≤1018,
∣
d
∣
≤
1
0
18
|d|≤10^{18}
∣d∣≤1018
输入样例:
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
1. 线段树维护区间gcd+树状数组维护单点值
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m;
const int maxn = 5e+5;
typedef long long ll;
ll a[maxn]; //原数组
ll dis[maxn]; //差分数组
ll C[maxn]; //树状数组
struct Node {
int l ,r;
ll gcd;
}tr[maxn*4]; //线段树维护差分数组的最大公约数
void pushup(int u)
{
tr[u].gcd = __gcd(tr[u<<1].gcd, tr[u<<1|1].gcd);
}
void build(int u, int l, int r)
{
if(l == r) {
tr[u] = {l,r,dis[r]};
return;
}
tr[u] = {l,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 v)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].gcd += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u<<1, x, v);
else modify(u<<1|1, x, v);
pushup(u);
}
ll query(int u,int l,int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].gcd;
int mid = tr[u].l + tr[u].r >> 1;
ll v = 0;
if(l <= mid) v = query(u<<1,l,r);
if(r > mid) v = __gcd(v,query(u<<1|1,l,r));
return v;
}
//树状数组
void update(int i,ll x)
{
while(i<=n) {
C[i] += x;
i += i & -i;
}
}
ll sum(int i)
{
ll res = 0;
while(i > 0){
res += C[i];
i -= i & -i;
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=n; ++i)
{
scanf("%lld",&a[i]);
dis[i] = a[i] - a[i-1];
update(i,dis[i]); //构造差分树状数组
}
build(1,1,n); //建立线段树
char op[2];
int l,r;
ll d;
while(m--)
{
scanf("%s%d%d", op,&l,&r);
if(*op == 'Q')
{
ll ans = __gcd(sum(l),query(1,l+1,r));
printf("%lld\n",ans<0?-ans:ans);
}
else
{
scanf("%lld",&d);
update(l,d);
update(r+1,-d);
modify(1,l,d);
if(r+1<=n)
modify(1,r+1,-d);
}
}
return 0;
}
2. 线段树维护两个属性(单点值 + 区间gcd)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m;
const int maxn = 5e+5;
typedef long long ll;
ll a[maxn]; //原数组
ll dis[maxn]; //差分数组
struct Node {
int l ,r;
ll gcd; //区间最大公约数
ll sum; //区间和
}tr[maxn*4]; //线段树维护差分数组的最大公约数
ll my_gcd(ll a,ll b)
{
return b?my_gcd(b,a%b):a;
}
void pushup(Node& u,Node& l,Node& r)
{
u.gcd = my_gcd(l.gcd, r.gcd);
u.sum = l.sum + r.sum;
}
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) {
tr[u] = {l,r,dis[r],dis[r]};
return;
}
tr[u] = {l,r,0,0};
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 v)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].gcd += v;
tr[u].sum += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u<<1, x, v);
else modify(u<<1|1, x, v);
pushup(u);
}
Node query(int u,int l,int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
if(r <= mid) return query(u<<1,l,r);
if(l > mid) return query(u<<1|1,l,r);
else
{
Node left = query(u<<1,l,r);
Node right = query(u<<1|1,l,r);
Node res;
pushup(res,left,right);
return res;
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=n; ++i)
{
scanf("%lld",&a[i]);
dis[i] = a[i] - a[i-1];
}
build(1,1,n); //建立线段树
char op[2];
int l,r;
ll d;
while(m--)
{
scanf("%s%d%d", op,&l,&r);
if(*op == 'Q')
{
ll ans = my_gcd(query(1,1,l).sum,query(1,l+1,r).gcd);
printf("%lld\n",ans);
}
else
{
scanf("%lld",&d);
modify(1,l,d);
if(r + 1 <= n) modify(1,r+1,-d);
}
}
return 0;
}