Step1 Problem:
给你 n 个数,有 m 次操作
操作分为两种类型:
ok == 1: 询问区间 L 到 R 的所有数的 gcd
ok != 1: 区间 L 到 R 都加 v
数据范围:
1 <= n,m <= 1e5, 1 <= L <= R <= n, 1 <= v <= 1000.
Step2 Ideas:
由于 gcd 的性质:
gcd(a, b) = gcd(a, a-b) 其中 a > b;
简单证明:
令 d = gcd(a, b);
a = d*t1; b = d*t2;
两式相减:a-b = d*(t1-t2),所以 gcd(a, a-b) = d
区间 L 到 R 的所有数的 gcd:gcd(w[L], W[L+1]-W[L], … , W[R]-W[R-1]);
区间 L 到 R 的所有数都加 v:L 位置加 v, R+1 位置减 v, [L, R-1] 位置由于 w[i]+v, w[i-1]+v, w[i]-w[i-1] 还是不变。
所以我们求出差分数组后,原本区间更新区间查询,就变成了单点更新区间查询。
差分数组:区间 1 到 L 的前缀和 = w[L]
gcd(-2, 2) = -2, 但是 -2 不是最大公约数,所以要变成 2.
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l+r)/2;
const int N = 1e5+5;
struct node
{
int sum, g;
};
node tree[N<<2];
int w[N], n;
node Merge(node x, node y)
{
node t;
t.sum = x.sum + y.sum;
t.g = __gcd(x.g, y.g);
return t;
}
void build(int root, int l, int r)
{
if(l == r) {
tree[root].g = tree[root].sum = w[l] - ((l-1)>0?w[l-1]:0);
return ;
}
MID;
build(lson, l, mid);
build(rson, mid+1, r);
tree[root] = Merge(tree[lson], tree[rson]);
}
int query_PreS(int root, int l, int r, int ul, int ur)
{
if(ul <= l && r <= ur)
return tree[root].sum;
int ret = 0;
MID;
if(mid >= ul) ret += query_PreS(lson, l, mid, ul, ur);
if(mid < ur) ret += query_PreS(rson, mid+1, r, ul, ur);
return ret;
}
int query_Gcd(int root, int l, int r, int ul, int ur)
{
if(ul > ur) return 0;//
if(ul <= l && r <= ur)
return tree[root].g;
int ret = 0;
MID;
if(mid >= ul) ret = __gcd(ret, query_Gcd(lson, l, mid, ul, ur));
if(mid < ur) ret = __gcd(ret, query_Gcd(rson, mid+1, r, ul, ur));
return ret;
}
void updata(int root, int l, int r, int pos, int v)
{
if(pos > n) return;//
if(l == r) {
tree[root].sum += v;
tree[root].g += v;
return ;
}
MID;
if(pos <= mid) updata(lson, l, mid, pos, v);
else updata(rson, mid+1, r, pos, v);
tree[root] = Merge(tree[lson], tree[rson]);
}
int main()
{
int m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", w+i);
build(1, 1, n);
int ok, L, R, v;
while(m--)
{
scanf("%d", &ok);
if(ok == 1)
{
scanf("%d %d", &L, &R);
//printf("%d %d\n", query_PreS(1, 1, n, 1, L), query_PreS(1, 1, n, 1, R));
printf("%d\n", abs(__gcd(query_PreS(1, 1, n, 1, L), query_Gcd(1, 1, n, L+1, R))));
}
else
{
scanf("%d %d %d", &L, &R, &v);
updata(1, 1, n, L, v);
updata(1, 1, n, R+1, -v);
}
}
return 0;
}