题目链接:https://www.acwing.com/problem/content/description/247/
可以用常数小的树状数组来求和,用线段树单点修改2333
这个题需要用到更相减损术
gcd(a,b) == gcd(a,b- a),数学归纳法可以推广gcd(a,b,c) == gcd(a,b - a,c -b)
这样我们可以维护一个b数组等于a[i] - a[i - 1],我们可以开心的发现每次在区间上加上一个d的时候b数组的b[l] += d;
b[r + 1] -= d;所以线段树只用两次单点修改,树状数组维护修改后a数组的值
我们还要考虑一点两个数的公约数我们规定是正数,我们在减的过程中会把数减成负数所以我们最重要返回他的绝对值
#include <bits/stdc++.h>
#define gcd __gcd
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
const ll maxn = 5e5 + 7;
const ll mod = 1e9 + 7;
struct node
{
ll l,r,dat;
}
t[maxn << 2];
ll a[maxn],b[maxn],c[maxn];
ll lowbit(ll x)
{
return x & -x;
}
void update(ll x,ll y)
{
for(int i = x; i <= maxn; i += lowbit(i))
{
c[i] += y;
}
}
ll getsum(ll x)
{
ll ans = 0;
for(int i = x; i > 0; i -= lowbit(i))
{
ans += c[i];
}
return ans;
}
void build(ll p,ll l,ll r)
{
t[p].l = l,t[p].r = r;
if(l == r){t[p].dat = b[l];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);
}
void change(ll p,ll x,ll y)
{
if(t[p].l == t[p].r){t[p].dat += y;return ;}
ll mid = (t[p].l + t[p].r) / 2;
if(mid >= x)change(p * 2,x,y);
else change(p * 2 + 1,x,y);
t[p].dat = gcd(t[p * 2].dat,t[p * 2 + 1].dat);
}
ll ask(ll p,ll l,ll r)
{
if(t[p].l >= l && t[p].r <= r)return t[p].dat;
ll mid = (t[p].l + t[p].r) / 2;
ll gc = 0;
if(mid >= l)gc = gcd(gc,ask(p * 2,l,r));
if(mid < r)gc = gcd(gc,ask(p * 2 + 1,l,r));
return abs(gc);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll n,m;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
b[i] = a[i] - a[i - 1];
}
build(1,1,n);
char c;
ll x,y,z;
for(int i = 1; i <= m; i ++)
{
cin >> c;
if(c == 'Q')
{
cin >> x >> y;
ll be = a[x] + getsum(x);
ll en = ask(1,x + 1,y);
cout << gcd(be,en) << endl;
}
else if(c == 'C')
{
cin >> x >> y >> z;
change(1,x,z);
if(y < n)change(1,y + 1,-z);
update(x,z);
update(y + 1,-z);
}
}
return 0;
}