(填坑)树状数组1

还记得当年纠结在树状数组的构成上半年。。。

一个万年老坑

说实话在这之前我没拍过一遍树状数组代码

--------------------------------------分割线--------------------------------------

进入正题(P.S.一下所有定义皆为从学习的博主转载而来,在结尾部分会上链接)

最普通的树状数组,利用lowbit的二进制对应方法,支持单点修改,1-i查询,但是这样的应用范围也太少了吧2333

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define N 3010
using namespace std;
int q,n,a[N];
inline int lowbit(int x){
    return (x&-x);
}
inline void add(int x,int del){
    for(int i=x;i<=n;i+=lowbit(i))
        a[i]+=del;
}
inline int sum_(int x){
    int sum=0;
    for(int i=x;i;i-=lowbit(i))
        sum+=a[i];
    return sum;
}
inline void Jimmy(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1,type;i<=q;i++){
        scanf("%d",&type);
        if(type==1){
            int x,del;
            scanf("%d%d",&x,&del);
            add(x,del);
        }
        if(type==2){
            int x;
            scanf("%d",&x);
            printf("%d\n",sum_(x)));
        }
    }
}
int main(){
    Jimmy();
    return 0;
} 

单点修改 1-i查询

那么我们接下来要介绍如何进行区间修改和区间查询

区间修改:

我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)

设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v

区间查询:

在基于树状数组的基础操作:单点修改、区间查询上,我们可以这样操作

首先观察

a[1]+a[2]+...+a[n]

= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])

= n*c[1] + (n-1)*c[2] +... +c[n]

= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])    (式子①)

那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]

每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变

那么

式子①

=n*sigma(c,n) - sigma(c2,n)

于是我们做到了在O(logN)的时间内完成一次区间和查询

岂不妙哉

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define N 3010
using namespace std;
int n,q,num[N],c1[N],c2[N];
inline int lowbit(int x){
    return (x&-x);
}
inline int add(int *r,int u,int del){
    for(int i=u;i<=n;i+=lowbit(i))
        r[i]+=del;
}
inline int sum_(int *r,int v){
    int sum=0;
    for(int i=v;i;i-=lowbit(i))    sum+=r[i];
    return sum;

}
inline void Jimmy(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&num[i]);
        add(c1,i,num[i]-num[i-1]);
        add(c2,i,(i-1)*(num[i]-num[i-1]));
    }
    for(int i=1,type;i<=q;i++){
        scanf("%d",&type);
        if(type==1){
            int u,v;
            scanf("%d%d%d",&u,&v,&w);
            add(c1,u,w);add(c1,v+1,-w);
            add(c2,u,(u-1)*w);add(c2,v+1,v*(-w));
        }
        if(type==2){
            int u,v;
            scanf("%d%d",&u,&v);
            int sum1=(u-1)*sum_(c1,u-1)-sum_(c2,u-1); //sigma(u-1)
            int sum2=v*sum_(c1,v)-sum_(c2,v);    //sigma(v)
            printf("%d\n",sum2-sum1);
        }
    }
}
int main(){
    Jimmy();
    return 0;
}

区间修改 区间查询

学习博客&&信息来源: http://blog.csdn.net/fsahfgsadhsakndas/article/details/52650026

时间: 2024-08-03 19:00:22

(填坑)树状数组1的相关文章

BZOJ 1227 [SDOI2009] 虔诚的墓主人 离线+树状数组+离散化

鸣谢:140142耐心讲解缕清了我的思路 题意:由于调这道题调的头昏脑涨,所以题意自己搜吧,懒得说. 方法:离线+树状数组+离散化 解析:首先深表本蒟蒻对出题人的敬(bi)意(shi).这道题简直丧心病狂,看完题后大脑一片空白,整个人都不好了,刚开始的思路是什么呢?暴力思想枚举每个墓碑,然后计算每个墓碑的虔诚度,然后再来统计.不过看看数据范围呢?10^9*10^9的矩阵,最多才10^5个树,光枚举就已经超时了,所以肯定不行.(不过要是考试真没思路我就那么搞了- -!) 然后也想到来枚举墓碑,不过

HDU 1556 Color the ball(树状数组)(填坑)

题目地址:HDU 1556 因为听别人说树状数组能做的线段树都可以,所以也一直没学,但是现在遇到好多题卡线段树...跪了..所以就学一下填填坑. 这题应该是树状数组的入门题了.不多说了. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #in

学习笔记——二维树状数组

不知道为什么,就是想把这个坑给填了... 二维树状数组,本质上还是树状数组,只是在一维的基础上变成了二维... 单点修改  1到i,j查询和一维基本一样,直接上代码 #include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #define N 3010 using namespace std; int a[N][N],n; inline int lowbit(int x

代码与算法集锦-归并排序+树状数组+快排+深度优先搜索+01背包(动态规划)

归并排序 求逆序数 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c

Luogu 45887 全村最好的嘤嘤刀(线段树 树状数组)

https://www.luogu.org/problemnew/show/T45887 题目背景 重阳节到了,我们最好的八重樱拥有全村最好的嘤嘤刀…… 题目描述 在绯玉丸力量的影响下,八重村成了一条长度为 nnn 的八重街,并且绯玉丸可以带着八重樱出现在街上的任意地点.而我们的八重樱则会在街上任意穿梭来获取某一地点上的嘤嘤嘤能量,用以升级她的嘤嘤刀. 在每个时刻,都会发生以下 333 个事件: 111 xxx valvalval 表示在 xxx 地点出现了携带着 valvalval 点嘤嘤嘤能

2019.9.25 初级数据结构——树状数组

一.树状数组基础 学OI的同学都知道,位运算(对二进制的运算)比普通运算快很多.同时我们接触到了状态压缩的思想(即将0-1的很多个状态压缩成十进制的一个数,每一个二进制位表示一个状态).由于在实际做题过程当中,由于数据范围限制,我们必须采用更高效的存储.查询方法,于是树状数组应运而生. 首先我们检查传统的存储状态.对于数组的每一个下标i,其所存储的有效信息只有一个a[i](这是传统数组).而对于树状数组,我们每一位下标可以存储lowbit(i)个有效信息.这个lowbit运算一会再说.所以尽管树

树状数组求逆序对:POJ 2299、3067

前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换相邻的两数使它有序,要你求出交换的次数.实质上就是求逆序对,网上有很多人说它的原理是冒泡排序,可以用归并排序来求出,但我一时间想不出它是如何和归并排序搭上边的(当初排序没学好啊~),只好用刚学过的树状数组来解决了.在POJ 1990中学到了如何在实际中应用上树状数组,没错,就是用个特殊的数组来记录即

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

树状数组(二)与poj2155 - matrix

今天下午,我进行了树状数组的进一步学习[1],并完成了poj2155的编程与调试,下面是一些记录与感想. 这道题目是一道二维树状数组的练习,中心思想如下: 1.C(x1, y1)(x2, y2)可以用c[x1][y1]++.c[x2 + 1][y1]++.c[x1][y1 + 1]--.c[x2 + 1][y2 + 1]--进行记录(证明与推理过程在注释[1]中). 2.Q(x, y)利用二维树状数组对c[1][1]到c[x][y]的累加和%2(即mod 2)求得(请各位参看注释[1]的资料自行