acwing246. 区间最大公约数(差分建树维护区间最大 gcd)

题意

  1. 给我们一个长度为 n 数组 A,m 次操作,两种操作如下:
    1. 把一个区间 [l, r] 中的数都加上 d
    2. 询问区间 [l, r] 中所有数的最大公因数是多少?

思路

  1. 由于区间 gcd 是满足结合行的,可以比较容易的维护区间 gcd ,可以用 rmq,线段树等,

  2. 但是这题又有区间修改操作,当一个区间被加上一个数之后,好像已经无法维护区间 gcd 了,因此我们需要曲线救国间接求解答案。

  3. 我们由辗转相除法可以知道:gcd (a,b)=gcd (a,b-a), 那么同理:(a+d, c+d, b+d)= (a+d, c, b). 那么当我们对区间 [l,r] 加 d 之后,

  4. 从上面的图片中式子我们可以看出,要想求左边式子的 gcd,我们可以通过求下面两个数的 gcd 式:
    在这里插入图片描述

  5. 我们可以通相邻两个数的差值,即差分建立树状数组维护区间操作,通过树状数组来求 a l + d a_l+d al+d, 然后在通过建立一颗线段树维护静态区间的 gcd, 即求第上图中的第二个数。

  6. 分别维护好了两个数就可以求解答案了。

代码

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ls (k << 1)
#define rs (k << 1 | 1)
const int N = 500005;
int n, m;
ll a[N];

struct Tree
{
    
    
    int l, r;
    ll sum, d; 
} tr[N * 4];

ll gcd(ll a, ll b) {
    
     return b ? gcd(b, a % b) : a; }

void push(Tree & a, Tree b, Tree c)
{
    
    
    a.sum = b.sum + c.sum;
    a.d = gcd(b.d, c.d);
}
void push_up(int k)
{
    
    
    push(tr[k], tr[ls], tr[rs]);
}

void build(int k, int l, int r)
{
    
    
    tr[k] = {
    
     l, r };
    if (l == r)
    {
    
    
        tr[k] = {
    
     l, r, a[l] - a[l - 1], a[l] - a[l - 1] };
        return;
    }

    int md = (l + r) >> 1;
    build(ls, l, md);
    build(rs, md + 1, r);
    push_up(k);
}

void modify(int k, int x, ll y)
{
    
    
    if (tr[k].l == tr[k].r)
    {
    
    
        ll z = tr[k].sum + y;
        tr[k] = {
    
     x, x, z, z };
        return;
    }

    int md = (tr[k].l + tr[k].r) >> 1;
    if (x <= md) modify(ls, x, y);
    else modify(rs, x, y);
    push_up(k);
}

Tree query(int k, int l, int r)
{
    
    
    if (l > r) return Tree{
    
     0 };
    if (tr[k].l >= l && tr[k].r <= r)
    {
    
    
        return tr[k];
    }

    int md = (tr[k].l + tr[k].r) >> 1;
    if (r <= md) return query(ls, l, r);
    else if (l > md) return query(rs, l, r);
    else
    {
    
    
        Tree t;
        push(t, query(ls, l, r), query(rs, l, r));
        return t;
    }
}


int main()
{
    
    
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
    build(1, 1, n);

    char op[2]; ll l, r, x;
    while (m --)
    {
    
    
        scanf("%s %lld %lld", op, &l, &r);
        if (* op == 'C')
        {
    
    
            scanf("%lld", &x);
            modify(1, l, x);
            if (r + 1 <= n) modify(1, r + 1, -x);
        }
        else
        {
    
    
            ll pre = query(1, 1, l).sum;
            ll suf = query(1, l + 1, r).d;
            printf("%lld\n", abs(gcd(pre, suf)));
        }
    }

    return 0;
}

おすすめ

転載: blog.csdn.net/qq_34261446/article/details/121142363