QBXT 求和

本文介绍了一种使用线段树解决QBXT求和问题的方法,通过维护两个数组的和与平方和,巧妙地处理了区间求和与更新操作,适用于处理大量查询与更新任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

QBXT 求和

Description

  • 给定两个长度为n的数列,有两种操作

    0 x y z表示将a数组第x个元素修改为y,b数组第x个元素修改为z

    1 l r 表示询问

    img

Input

  • 第一行两个正整数n,m

    第二行n个数 a1,a2,…,an

    第三行n个数 b1,b2,…,bn

    接下来m行,表示m次操作

Output

  • 对于每个询问操作,输出答案。

Sample Input

5 3

1 2 3 4 5

1 2 3 4 5

1 2 4

0 3 6 6

1 2 4

Sample Output

26
44

Data Size

  • 30% n,m<=100

    50% n,m<=4000

    另20% 没有修改操作

    另20% 保证 ai=bi

    100% n,m<=100000

    保证1<=ai,bi<=10000

题解:

  • 线段树。
  • 首先考虑暴力。50pts直接线段树暴力。考虑后20%数据ai = bi的情况。对于这种情况,如果要求区间[l, r],答案是:( sigma[l到r之和] ^ 2 - sigma[l到r的平方和] ) / 2。
  • 为什么?初中数学告诉我们:(a + b + c + d + ...) ^ 2 = a ^ 2 + b ^ 2 + c ^ 2 + d ^ 2 + ... + 2(ab + ac + ad + bc + bd + cd + ...)。
  • 那么我们要求的就是ab + ac + ad + bc + bd + cd + ...嘛,于是开两个线段树分别维护sigma区间和 and sigma区间平方和即可。
  • 考虑正解。
  • 这题正解还挺妙的,巧妙利用了线段树的本质。

  • 对于一个区间[l, r],算出mid = (l + r) / 2,所求就是[l, r]的答案。
  • 因为:l <= i < j <= r;所以:对于点对(i, j)有3种情况。
  1. 1 <= i < j <= mid
  2. mid < i < j <= r
  3. 1 <= i <= mid, mid < j <= r
  • 那么显然ans = 情况1的ans + 情况2的ans + 情况3的ans。

  • 建立线段树。有t[p].l(区间左边界)、t[p].r(区间右边界)、t[p].sum(所求区间的答案)、t[p].v1(a数组中对应区间之和)、t[p].v2(b数组中对应区间之和)。
  • 因为:t[p].sum = 情况1的ans + 情况2的ans + 情况3的ans
  • 情况1的ans = t[p << 1].sum;情况2的ans = t[p << 1 | 1].sum
  • 情况3我举个例子,你们来理解:

a1 a2 a3 | a4 a5 a6

b1 b2 b3 | b4 b5 b6

'|' 为mid

  • 这时情况3的ans = a1b4 + a1b5 + a1b6 + a2b4 + a2b5 + a3b6 = (a1 + a2 + a3) * (b4 + b5 + b6)。
  • 所以一般性的情况下,情况3的ans = t[p << 1].v1 * t[p << 1 | 1].v2

  • 综上所述,这一题主要核心就是在pushup操作的改变,下面给出pushup的代码:

t[p].v1 = t[p << 1].v1 + t[p << 1 | 1].v1;

t[p].v2 = t[p << 1].v2 + t[p << 1 | 1].v2;

t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum + t[p << 1].v1 * t[p << 1 | 1].v2;

  • 最后提醒一下,由于sum的转移并不是直接累加,所以查询操作的时候不能直接返回ask(...) + ask(...)。
#include <iostream>
#include <cstdio>
#define N 100005
#define int long long
using namespace std;

struct T {int l, r, v1, v2, sum;} t[N * 4];
int n, m;
int a[N], b[N];

int read()
{
    int x = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x;
}

void up(int p)
{
    t[p].v1 = t[p << 1].v1 + t[p << 1 | 1].v1;
    t[p].v2 = t[p << 1].v2 + t[p << 1 | 1].v2;
    t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum + t[p << 1].v1 * t[p << 1 | 1].v2;
}

void build(int p, int l, int r)
{
    t[p].l = l, t[p].r = r;
    if(l == r) {t[p].v1 = a[l], t[p].v2 = b[l]; return;}
    int mid = (l + r) >> 1;
    build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
    up(p);
}

void upd(int p, int l, int r, int v1, int v2)
{
    if(t[p].l >= l && t[p].r <= r)
        {t[p].v1 = v1, t[p].v2 = v2, t[p].sum = 0; return;}
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid) upd(p << 1, l, r, v1, v2);
    if(r > mid) upd(p << 1 | 1, l, r, v1, v2);
    up(p);
}

int reAsk(int p, int l, int r, int op)
{
    if(t[p].l >= l && t[p].r <= r)
    {
        if(op == 1) return t[p].v1;
        else return t[p].v2;
    }
    int mid = (t[p].l + t[p].r) >> 1, ans = 0;
    if(l <= mid) ans += reAsk(p << 1, l, r, op);
    if(r > mid) ans += reAsk(p << 1 | 1, l, r, op);
    return ans;
}

int ask(int p, int l, int r)
{
    if(t[p].l >= l && t[p].r <= r) return t[p].sum;
    int mid = (t[p].l + t[p].r) >> 1;
    if(r <= mid) return ask(p << 1, l, r);
    if(l > mid) return ask(p << 1 | 1, l, r);
    return ask(p << 1, l, mid) + ask(p << 1 | 1, mid + 1, r) +
           reAsk(p << 1, l, r, 1) * reAsk(p << 1 | 1, l, r, 2);
}

signed main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i <= n; i++) b[i] = read();
    build(1, 1, n);
    for(int i = 1; i <= m; i++)
    {
        int op = read();
        if(!op)
        {
            int pos = read(), v1 = read(), v2 = read();
            upd(1, pos, pos, v1, v2);
        }
        else
        {
            int l = read(), r = read();
            printf("%lld\n", ask(1, l, r));
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/BigYellowDog/p/11625595.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值