其他分享
首页 > 其他分享> > AcWing 246. 区间最大公约数(线段树 + 树状数组)

AcWing 246. 区间最大公约数(线段树 + 树状数组)

作者:互联网

一个gcd(最大公约数)的推论:
gcd(x_l, x_{l+1}, x_{l+2}, ...x_r)=gcd(x_l, x_{l+1}-x_{l}, x_{l+2}- x_{l+1},..., x_r-x_{r-1})

而且,x_i-x_{i-1}其实就是x_i的差分,记为tr_i

算法设计:

分别另外建立一个差分序列tr_i,用线段树和数组维护前缀和.

初始化:add(i, a[i] - a[i - 1])build(1, 1, n + 1)

1.对于"C l r d"指令,(树状数组)add(l, d), add(r + 1, -d), (线段树)change(l, d), change(r + 1, -d).

2.对于"Q l r"指令,res = gcd(sum(l), ask(1, l + 1, r))(前者是树状数组求和,后者是线段树)

代码实现如下:

#include <bits/stdc++.h>
// #define LOCAL
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define debug(a) cout << #a << "=" << a << endl;
using namespace std;
const int N = 5e5 + 20;
int a[N], b[N], tr[N];
struct tr_{
    int l, r, dat;
    #define l(x) (x) << 1
    #define r(x) (x) << 1 | 1
}t[N * 4];
int n, m;

int ask(int x){
    int ans = 0;
    for (; x; x -= x & -x)
        ans += tr[x];
    return ans;
}
void add(int x, int y){
    for (; x <= n; x += x & -x)
        tr[x] += y;
}

void build(int p, int l, int r){
    t[p].l = l, t[p].r = r;
    if (l == r){
        t[p].dat = b[l];
        return;
    }
    int mid = l + r >> 1;
    build(l(p), l, mid);
    build(r(p), mid + 1, r);
    t[p].dat = __gcd(t[l(p)].dat, t[r(p)].dat);
}

void change(int p, int x, int d){
    if (t[p].l == t[p].r){
        t[p].dat += d;
        return;
    }
    int mid = t[p].l + t[p].r >> 1;
    if (x <= mid)
        change(l(p), x, d);
    else
        change(r(p), x, d);
    t[p].dat = __gcd(t[l(p)].dat, t[r(p)].dat);
}

int ask(int p, int l, int r){
    if (l > r)
        return 0;
    if (l <= t[p].l && r >= t[p].r)
        return t[p].dat;
    int mid = t[p].l + t[p].r >> 1;
    int val = 0;
    if (l <= mid)
        val = __gcd(ask(l(p), l, r), val);
    if (r > mid)
        val = __gcd(ask(r(p), l, r), val);
    return val;
}
signed main(){
#ifdef LOCAL
    freopen("input.in", "r", stdin);
    freopen("output.out", "w", stdout);
#endif
    IOS;
    cin >> n >> m;
    //1.初始化差分数组,并对差分数组建立一个树状数组和线段树
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
        b[i] = a[i] - a[i - 1];
        add(i, b[i]);
    }
    build(1, 1, n + 1);
    while (m--){
        string op;
        cin >> op;
        if (op == "C"){
            int l, r, d;
            cin >> l >> r >> d;
            add(l, d), add(r + 1, -d);//对树状数组进行操作
            change(1, l, d), change(1, r + 1, -d);
        }else{
            int l, r;
            cin >> l >> r;
            cout << abs(__gcd(ask(l), ask(1, l + 1, r))) << '\n';
        }
    }
}

标签:int,add,mid,dat,最大公约数,246,数组,define,AcWing
来源: https://blog.csdn.net/CK1513710764/article/details/123606570