【线段树 && 区间更新 求区间 gcd】HYSBZ - 5028 小Z的加油店

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值