树状数组板子

板子1

板子2

在放板子的代码之前,先讲一下树状数组。

树状数组的作用:

在有修改时可以做到log级别求前缀和

还可以结合差分等神奇的东西食用

空间比线段树要省的多,代码量也少的多

在单点查询的时候比线段树快了不是一点(我真的没有拿线段树的板子去拍这两个题)

我们先来看一下树状数组是个什么东西

首先,我们有一个序列A,其次lowbit(x)表示x的二进制表示中,最低位的1和后面的0构成的数。

树状数组c[i]记录序列A的区间[i-lowbit(i)+1,x]中所有数的和。

why?我们画一画树状数组

它是不是很像一棵树

同时我们可以发现一些性质:

每个c[x]保存了以它为根的子树中叶节点的和

c[x]的子节点数=lowbit(x)

c[x]的父亲是c[x+lowbit(x)]

树深度为logn

占用空间:O(n)

lowbit好像很重要的样子,so,lowbit怎么求呢

lowbit(x)=x&(~x+1)=x&-x,因为~x+1=-x。

why?

举个例子(设x>0,这里牵扯到树状数组不能处理下标为0的情况)

树状数组支持两种神奇的修改与查询。

一.单点修改,区间查询(板子1)

二.区间修改,单点查询(板子2)

先说一说第一种

前面提到过c[x]的父亲是c[x+lowbit(x)],所以更改的代码就是这样:

void update(int x,int v)
{
    for(;x<=n;x+=x&(-x))c[x]+=v;
}

因为c数组是维护的前缀和,所以当查询区间[l,r]的时候,我们可以算出sum(r)-sum(l-1),这里sum(i)是[1,i]的和。

怎么算sum(i)?

因为c[x]记录的是[lowbit(x)+1,x]这个区间的和,为了统计[1,x],所以我们每次x要减去lowbit(x)。

代码

int sum(int x)
{
    int ans=0;
    for(;x>=1;x-=x&(-x))ans+=c[x];
    return ans;
}

再来说一说第二种神奇的操作(区间修改,单点查询)我真的没有拿线段树的板子做这道题

我们先看一个东西:差分

现在有一个a数组:a[1],a[2]……a[n]

还有一个b数组:b[1]=a[1]-a[0],b[2]=a[2]-a[1]……b[i]=a[i]-a[i-1]

b数组就叫做差分数组。

通过差分数组的定义,我们知道:a[2]=a[1]+b[2]=b[1]+b[2],a[3]=a[2]+b[3]=b[1]+b[2]+b[3]……

我们发现了差分的一个性质:a[i]=b[1]+b[2]+……+b[i]

那差分有什么用呢?

我们想到这里是区间修改,如果把区间中的每一个点当做单点修改,复杂度O(nlogn),好像还不如暴力,而且树状数组也木有懒标记之类的操作,这时就要用到差分数组了。

若我们让[l,r]这个区间每个数都加上k,则对于差分数组来说,会变的只有b[l],b[r+1]。因为a[l]加了k,而a[l-1]不变,所以b[l]增加了k,a[r]增加了k,a[r+1]不变,所以b[r+1]减少k。

当单点查询时,就是询问前缀和(也就是上面差分的性质)

so,我们只是把树状数组维护的对象从a数组变成了它的差分数组b数组而已,其他的不变。

这里放上板子

#include<bits/stdc++.h>//这个是板子2,板子1也差不多
using namespace std;
int n,m,c[5000009],a[500009];
int read()
{
    char ch=getchar();
    int x=0;bool f=0;
    while(ch<‘0‘||ch>‘9‘)
    {
        if(ch==‘-‘)f=1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘)
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f?-x:x;
}
void update(int x,int v)
{
    for(;x<=n;x+=x&(-x))c[x]+=v;
}
int sum(int x)
{
    int ans=0;
    for(;x>=1;x-=x&(-x))ans+=c[x];
    return ans;
}
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
      a[i]=read();
    for(int i=1;i<=n;i++)
      update(i,a[i]-a[i-1]);  //在初始化时,直接在i的位置加上a[i]-a[i-1]
    for(int i=1;i<=m;i++)
    {
        int cz=read(),x=read();
        if(cz==1)
         {
             int y=read(),k=read();
             update(x,k);
             update(y+1,-k);
         }
        else
          printf("%d\n",sum(x));
    }
}

原文地址:https://www.cnblogs.com/lcez56jsy/p/11128498.html

时间: 2024-10-14 03:37:33

树状数组板子的相关文章

BZOJ3289[JZYZOJP2018]: Mato的文件管理 莫队+树状数组+离散化

        描述 Description     Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能

树状数组浅讲

树状数组浅讲 rt,个人肤浅理解,各位神犇请自动出门右转 不同于传统数组每个元素单独存放,求和时遍历相加,树状数组每个元素不单独维护,而是被维护在一个包含其他元素的前缀和里.宜先仔细揣摩后再行. 上图便体现了树状数组储存数据的原理 相当于以下等式 说明:C[]是树状数组,A[]是实际元素 C[1]=A[1]; C[2]=A[1]+A[2]; C[3]=A[3]; C[4]=A[1]+A[2]+A[3]+A[4]; C[5]=A[5]; C[6]=A[5]+A[6]; C[7]=A[7]; C[8

HDU 5249 离线树状数组求第k大+离散化

KPI Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1160    Accepted Submission(s): 488 Problem Description 你工作以后, KPI 就是你的全部了. 我开发了一个服务,取得了很大的知名度.数十亿的请求被推到一个大管道后同时服务从管头拉取请求.让我们来定义每个请求都有一个重要值.我的

洛谷 P3374 【模板】树状数组 1 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=3374 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操

[COGS257]动态排名系统 树状数组套主席树

257. 动态排名系统 时间限制:5 s   内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第k的数.2.修改A[i]的值为j.所谓排名第k,指一些数按照升序排列后,第k位的数.例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9.[输入格式]第一行包含一个整数D(0<=

洛谷 P3374 【模板】树状数组 1 如题(单点修改+区间查询)

P3374 [模板]树状数组 1 时空限制1s / 128MB 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x k 含义:将第x个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内

洛谷 P3368 【模板】树状数组 2 如题(区间修改+单点查询)

P3368 [模板]树状数组 2 时空限制1s / 128MB 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出

luoguP2184 贪婪大陆 题解(树状数组)

P2184 贪婪大陆  题目 其实很容易理解就是询问一段区间内有多少段不同的区间 然后再仔细思索一下会发现: 1.只要一个区间的开头在一个节点i的左边,那么这个区间包含在区间1~i中. 2.只要一个区间的尾部在一个节点j的左边,那么这个区间肯定不属于j之后的所有区间 这时候就不难想到用两个树状数组维护: 第一个:维护节点i之前有多少个区间的开头 第二个:维护节点j之前有多少个区间的结尾 不难证明拿sum[i]-sum[j]得到的就是i~j中间地雷的个数(手动模拟一波就一清二楚了) #includ

BZOJ 3262: 陌上花开 cdq分治 树状数组

https://www.lydsy.com/JudgeOnline/problem.php?id=3262 cdq分治板子题,一维排序,一维分治(cdq里的队列),一维数据结构(树状数组). 学dp优化前来复习--以前好像写过这道题但是没写博客啊--在校oj上写的题都没怎么写博客,追悔莫及 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #