1264. 动态求连续区间和(树状数组---某个位置加上一个数/求在线(动态)前缀和/蓝桥杯)

题目:

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35

 树状数组:

代码:

#include<cstdio>
#include<iostream>
using namespace std;

const int N=100010;
int n,m;
int a[N],tr[N];

//2^k
int lowbit(int x)
{
    return x&-x;
}

//改变数组在位置x上的值(加上某个值)
void add(int x,int v)
{
    for(int i=x;i<=n;i+=lowbit(i))tr[i]+=v;
}

//区间查询(求动态(在线)前缀和)
int query(int x)
{
    int res=0;
    for(int i=x;i>0;i-=lowbit(i))res+=tr[i];
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)add(i,a[i]);//求得tr[i]
    
    while(m--){
        int k,a,b;
        scanf("%d%d%d",&k,&a,&b);
        if(k==1)add(a,b);
        else
        printf("%d\n",query(b)-query(a-1));//在线区间和
    }
    return 0;
}

线段树:

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n, m;
int w[N];
struct Node
{
    int l, r;//左右区间
    int sum;//区间值
}tr[N * 4];//创建树结点

//求结点大小(由左右儿子结点大小和得到)
void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

//初始化线段树各个结点(从根结点1开始依次往下递归至叶结点)
void build(int u, int l, int r)
{
    if (l == r) tr[u] = {l, r, w[r]};//递归到叶结点
    else
    {
        tr[u] = {l, r};//为当前结点的左右区间赋值
        int mid = l + r >> 1;//二分成左右儿子。分头初始化
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);//为当前结点大小赋值
    }
}

//区间查询(递归求在线区间和(u表示树当前结点位置,l、r表示要求的区间范围))
int query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;//区间完全包含当前结点的区间
    
    //不完全包含
    int mid = tr[u].l + tr[u].r >> 1;//结点区间二分
    int sum = 0;
    if (l <= mid) sum = query(u << 1, l, r);//与左儿子有交集
    if (r > mid) sum += query(u << 1 | 1, l, r);//与右儿子有交集
    return sum;
}

//单点修改(u表示树当前的结点位置,x为要修改的地方,v为要在x处加上的值)
void modify(int u, int x, int v)
{
    if (tr[u].l == tr[u].r) tr[u].sum += v;//递归到了叶结点(即找到了要修改的位置x)
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;//树的结点区间二分
        if (x <= mid) modify(u << 1, x, v);//x在左儿子部分
        else modify(u << 1 | 1, x, v);//x在右儿子部分
        pushup(u);//为修改后的当前结点赋值
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    build(1, 1, n);//初始化线段树各个结点

    int k, a, b;
    while (m -- )
    {
        scanf("%d%d%d", &k, &a, &b);
        if (k == 0) printf("%d\n", query(1, a, b));
        else modify(1, a, b);
    }

    return 0;
}

总结:

线段树和树状数组都是用来解决区间查询和更新的数据结构,但它们各自有不同的适用场景和问题解决能力。

线段树适用于解决静态的区间查询和更新问题,比如求区间最大值、最小值、区间和等。它可以高效地进行区间查询和更新操作,时间复杂度为O(log n)。线段树常用于解决动态规划、区间覆盖等问题。

树状数组适用于解决动态的单点查询和区间更新问题,比如求前缀和、区间和等。它可以高效地进行单点查询和区间更新操作,时间复杂度为O(log n)。树状数组常用于解决动态规划、离散化等问题。

总的来说,线段树适用于静态的区间查询和更新问题,而树状数组适用于动态的单点查询和区间更新问题。在实际应用中,可以根据具体的问题特点选择合适的数据结构来解决。

<think>好的,我现在需要帮助用户查找蓝桥杯比赛中关于棋盘问题的树状数组解法或实现代码。首先,我要确定用户的需是什么。用户提到了“棋盘问题”树状数组”,可能需要解决的是二维区间操作问题,比如格子修改或查询,这在蓝桥杯的题目中比较常见,比如《棋盘上的问题》或《格子刷油漆》等题目。 首先,我需要回忆树状数组(Fenwick Tree)的基本知识。树状数组通常用于高效处理前缀和查询单点更新,时间复杂度为O(log n)。但在棋盘问题中,可能涉及到二维的情况,这时候需要使用二维树状数组,可以处理二维区域的操作,比如子矩阵的更新。 接下来,我需要考虑蓝桥杯历届真题中是否有类似的题目。根据引用[1][2],用户提供的参考资料可能涉及C++Python的代码,但具体到棋盘树状数组的结合,可能需要进一步查找相关题解。例如,有些题目可能要动态维护棋盘的状态,进行多次区间增减操作,并查询某个区域的或极值。这时候树状数组的优势就体现出来了。 然后,用户需要解题思路实现代码。针对二维棋盘问题,树状数组的二维实现是关键。每个节点(i,j)维护的是一个二维的前缀和信息。当进行区间更新时,可能需要使用差分的方法,将区间操作转化为四个单点操作,再利用树状数组的高效更新查询。 例如,假设题目要对棋盘的一个子矩阵进行增加某个值,然后查询某个点的值。这时候可以用二维树状数组结合差分的方法。具体来说,对于每个更新操作(x1,y1,x2,y2)加上v,可以通过四次更新操作实现:在(x1,y1)加v,(x1,y2+1)减v,(x2+1,y1)减v,(x2+1,y2+1)加v。这样,查询某个点(x,y)的值时,只需计算前缀和即可得到当前值。 接着,代码实现部分需要构建二维树状数组的结构。在Python中,可以初始化一个二维组,并实现updatequery方法。需要注意的是,树状数组的下标通常从1开始,因此在处理输入据时可能需要调整坐标。 验证思路的正确性也很重要。例如,考虑一个小棋盘,手动模拟几次更新查询操作,确保结果符合预期。同时,需要注意边界条件,比如当更新区域超出棋盘范围时的处理。 最后,根据用户的引用资料,引用[1]提到了C++中的unordered_map用于节点答案的存储,可能与本题无关,但引用[2]中的Python处理大的方法可能在输入时有用,但树状数组的实现本身不需要处理大,所以主要关注树状数组的结构操作。 总结步骤: 1. 确定问题类型:二维区间更新单点查询。 2. 使用二维树状数组结合差分方法处理区间更新。 3. 实现二维树状数组的updatequery函。 4. 编写代码框架,处理输入输出,并测试样例。 可能的挑战包括正确实现二维树状数组的更新查询逻辑,以及差分方法的正确应用。需要仔细检查每个步骤,避免逻辑错误。</think>关于蓝桥杯比赛中棋盘问题与树状数组的结合应用,以下为典型解题思路及实现代码: --- ### 问题特征 棋盘问题通常涉及**二维区间操作**,例如: - 多次对子矩阵执行增减操作 - 查询单点或子矩阵的值 - 统计符合特定条件的区域 树状数组(Fenwick Tree)通过$O(\log n)$时间复杂度实现高效的前缀和计算与单点更新,适合处理这类高频操作的问题[^1]。 --- ### 解题思路(以子矩阵增减+单点查询为例) 1. **差分思想** 将二维区间增减转化为四个单点操作: $$ \text{update}(x1,y1,+v),\ \text{update}(x1,y2+1,-v) $$ $$ \text{update}(x2+1,y1,-v),\ \text{update}(x2+1,y2+1,+v) $$ 2. **二维树状数组实现** 维护差分组的前缀和,实现单点修改与区域查询: ```python class Fenwick2D: def __init__(self, n, m): self.n = n self.m = m self.tree = [[0]*(m+1) for _ in range(n+1)] def update(self, x, y, delta): while x <= self.n: y1 = y while y1 <= self.m: self.tree[x][y1] += delta y1 += y1 & -y1 x += x & -x def query(self, x, y): res = 0 while x > 0: y1 = y while y1 > 0: res += self.tree[x][y1] y1 -= y1 & -y1 x -= x & -x return res ``` 3. **操作映射** - **子矩阵更新**:调用四次`update()`实现差分 - **单点查询**:直接计算`query(x,y)` --- ### 完整代码实现(Python) ```python class Fenwick2D: def __init__(self, n, m): self.n = n self.m = m self.tree = [[0]*(m+2) for _ in range(n+2)] # 下标从1开始 def update(self, x, y, delta): while x <= self.n: y1 = y while y1 <= self.m: self.tree[x][y1] += delta y1 += y1 & -y1 x += x & -x def query(self, x, y): res = 0 while x > 0: y1 = y while y1 > 0: res += self.tree[x][y1] y1 -= y1 & -y1 x -= x & -x return res # 输入棋盘大小n x m,操作次q n, m = map(int, input().split()) q = int(input()) fw = Fenwick2D(n, m) for _ in range(q): op = list(map(int, input().split())) if op[0] == 1: # 子矩阵更新: op1 x1 y1 x2 y2 v x1, y1, x2, y2, v = op[1], op[2], op[3], op[4], op[5] fw.update(x1, y1, v) fw.update(x1, y2+1, -v) fw.update(x2+1, y1, -v) fw.update(x2+1, y2+1, v) else: # 单点查询: op2 x y x, y = op[1], op[2] print(fw.query(x, y)) ``` --- ### 应用场景 1. 蓝桥杯《格子刷油漆》题型中动态维护棋盘状态 2. 统计符合条件(如异或特征)的区域量 3. 解决多次操作后的棋盘极值问题 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hz2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值