作者:林立霞61556 | 来源:互联网 | 2024-10-19 11:34
http:blog.csdn.netwzw1376124061articledetails731882382017-06-1317:2464人阅读评论(0)收藏举报版权声明:!!!
http://blog.csdn.net/wzw1376124061/article/details/73188238
2017-06-13 17:24
64人阅读
评论(0)
收藏
举报
版权声明:!!!本文为博主原创文章,若转载请附博主链接,一经发现盗版将追究责任。!!!
1、概述
树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?
2、树状数组基本操作
传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。
给定序列(数列)A,我们设一个数组C满足
C[i] = A[i–2^k+ 1] + … + A[i]
其中,k为i在二进制下末尾0的个数,i从1开始算!
则我们称C为树状数组。
下面的问题是,给定i,如何求2^k?
答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)
下面进行解释:
以i=6为例(注意:a_x表示数字a是x进制表示形式):
(i)_10 = (0110)_2
(i-1)_10=(0101)_2
i xor (i-1) =(0011)_2
i and (i xor (i-1)) =(0010)_2
2^k = 2
C[6] = C[6-2+1]+…+A[6]=A[5]+A[6]
数组C的具体含义如下图所示:
当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。
树状数组能快速求任意区间的和:A[i] + A[i+1] + … + A[j],设sum(k) = A[1]+A[2]+…+A[k],则A[i] + A[i+1] + … + A[j] = sum(j)-sum(i-1)。
已知一个数列,你需要进行下面两种操作:
1.将某一个数加上x
2.求出某区间每一个数的和
[cpp]
view plain
copy
print
?
- /*************************************************************************
- > Author: wzw-cnyali
- > Created Time: 2017/6/13 10:33:11
- ************************************************************************/
-
- #prag\
- ma GCC optimize(“O3”)
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- using namespace std;
-
- typedef long long LL;
-
- typedef unsigned long long uLL;
-
- #define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
- #define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; &#8212; i)
- #define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
- #define debug(&#8230;) fprintf(stderr, __VA_ARGS__)
- #define mem(a, b) memset((a), b, sizeof(a))
-
- template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
- template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
-
- char buff[1 << 25], *buf = buff;
-
- template <class T>
- T read(T sum = 0, T fg = 0)
- {
- while(*buf < &#8216;0&#8217; || *buf > &#8216;9&#8217;) { fg |= *buf == &#8216;-&#8216;; buf++; }
- while(*buf >= &#8216;0&#8217; && *buf <= &#8216;9&#8217;) { sum = sum * 10 + *buf &#8211; &#8216;0&#8217;; buf++; }
- return fg ? -sum : sum;
- }
-
- const int inf = 1e9;
-
- const LL INF = 1e17;
-
- const int Size = 500010;
-
- const int maxn = 100000;
-
- const int maxm = 100000;
-
- int n, m;
- int a[Size];
-
- struct index_tree
- {
- #define lowbit(x) ((x) & (-x))
- void add(int x, int val)
- {
- for(; x <= n; x += lowbit(x)) a[x] += val;
- }
- int query(int x)
- {
- int ans = 0;
- for(; x; x -= lowbit(x)) ans += a[x];
- return ans;
- }
- }tree;
-
- int main()
- {
- #ifndef ONLINE_JUDGE
- freopen(&#8220;input.in&#8221;, &#8220;r&#8221;, stdin);
- freopen(&#8220;output.out&#8221;, &#8220;w&#8221;, stdout);
- #endif
- fread(buff, 1, 1 << 25, stdin);
- n = read<int>(), m = read<int>();
- REP(i, 1, n) tree.add(i, read<int>());
- while(m&#8211;)
- {
- int tp = read<int>();
- if(tp == 1)
- {
- int x = read<int>(), val = read<int>();
- tree.add(x, val);
- }
- else
- {
- int x = read<int>(), y = read<int>();
- printf(&#8220;%d\n&#8221;, tree.query(y) &#8211; tree.query(x &#8211; 1));
- }
- }
- return 0;
- }
题目链接:
https://www.luogu.org/problem/show?pid=3374
已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数数加上x
2.求出某一个数的值
思路:
这里要引入差分数组这种东西,我们记d[i] = a[i] &#8211; a[i-1](a为原数组),这样我们记sigma(d[i]) = a[i] ,为什么呢,观察式子sigma(d[i]) = a[1] + a[2] &#8211; a[1] +a[3]...这样一直下去就得到了我们的原数组。
有什么用呢?如果我们往一段区间上加k,在差分数组上如何体现呢?我们举个例子:
a:1,2,3,4,5
d:1,1,1,1,1
2~4加1
如果我们盲目的在2到4上加1,就会发现会影响后面的数(因为是前缀和),所以我们在2这个位置加一,用树状数组更新,在5的位置减一用树状数组更新就ok了
a:1,3,4,5,5
d:1,2,1,1,0
[cpp]
view plain
copy
print
?
- /*************************************************************************
- > Author: wzw-cnyali
- > Created Time: 2017/6/13 11:11:16
- ************************************************************************/
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- #prag\
- ma GCC optimize(&#8220;O3&#8221;)
-
- using namespace std;
-
- typedef long long LL;
-
- typedef unsigned long long uLL;
-
- #define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
- #define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; &#8212; i)
- #define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
- #define debug(&#8230;) fprintf(stderr, __VA_ARGS__)
- #define mem(a, b) memset((a), b, sizeof(a))
-
- template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
- template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
-
- char buff[1 << 25], *buf = buff;
-
- template <class T>
- T Fread(T sum = 0, T fg = 0)
- {
- while(*buf < &#8216;0&#8217; || *buf > &#8216;9&#8217;) { fg |= *buf == &#8216;-&#8216;; buf++; }
- while(*buf >= &#8216;0&#8217; && *buf <= &#8216;9&#8217;) { sum = sum * 10 + *buf &#8211; &#8216;0&#8217;; buf++; }
- return fg ? -sum : sum;
- }
-
- template <class T>
- T read(T sum = 0, T fg = 0)
- {
- char c = getchar();
- while(c < &#8216;0&#8217; || c > &#8216;9&#8217;) { fg |= c == &#8216;-&#8216;; c = getchar(); }
- while(c >= &#8216;0&#8217; && c <= &#8216;9&#8217;) { sum = sum * 10 + c &#8211; &#8216;0&#8217;; c = getchar(); }
- return fg ? -sum : sum;
- }
-
- const int inf = 1e9;
-
- const LL INF = 1e17;
-
- const int Size = 500010;
-
- const int maxn = 100000;
-
- const int maxm = 100000;
-
- int n, m;
- int a[Size];
-
- struct index_tree
- {
- #define lowbit(x) ((x) & (-x))
- int tree[Size];
-
- void add(int x, int val)
- {
- for(; x <= n; x += lowbit(x)) tree[x] += val;
- }
-
- int query(int x)
- {
- int ans = 0;
- for(; x; x -= lowbit(x)) ans += tree[x];
- return ans;
- }
- }tree;
-
- int main()
- {
- #ifndef ONLINE_JUDGE
- freopen(&#8220;input.in&#8221;, &#8220;r&#8221;, stdin);
- freopen(&#8220;output.out&#8221;, &#8220;w&#8221;, stdout);
- #endif
- fread(buff, 1, 1 << 25, stdin);
- n = Fread<int>(), m = Fread<int>();
- REP(i, 1, n)
- {
- a[i] = Fread<int>();
- tree.add(i, a[i] &#8211; a[i &#8211; 1]);
- }
- while(m&#8211;)
- {
- if(Fread<int>() == 1)
- {
- int x = Fread<int>(), y = Fread<int>(), val = Fread<int>();
- tree.add(x, val);
- tree.add(y + 1, -val);
- }
- else printf(&#8220;%d\n&#8221;, tree.query(Fread<int>()));
- }
- return 0;
- }
题目链接:https://www.luogu.org/problem/show?pid=3368
已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
首先观察式子:
a[1]+a[2]+&#8230;+a[n]
= (c[1]) + (c[1]+c[2]) + &#8230; + (c[1]+c[2]+&#8230;+c[n])
= n*c[1] + (n-1)*c[2] +&#8230; +c[n]
= n * (c[1]+c[2]+&#8230;+c[n]) &#8211; (0*c[1]+1*c[2]+&#8230;+(n-1)*c[n]) (式子①)
那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]
每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变
那么式子①=n*sigma(c,n) &#8211; sigma(c2,n)
于是我们做到了在O(logN)的时间内完成一次区间和查询
一件很好的事情就是树状数组的常数比其他NlogN的数据结构小得多,实际上它的计算次数比NlogN要小很多,再加上它代码短,是OI中的利器
[cpp]
view plain
copy
print
?
- /*************************************************************************
- > Author: wzw-cnyali
- > Created Time: 2017/6/13 15:18:18
- ************************************************************************/
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- #prag\
- ma GCC optimize(&#8220;O3&#8221;)
-
- using namespace std;
-
- typedef long long LL;
-
- typedef unsigned long long uLL;
-
- #define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
- #define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; &#8212; i)
- #define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
- #define debug(&#8230;) fprintf(stderr, __VA_ARGS__)
- #define mem(a, b) memset((a), b, sizeof(a))
-
- template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
- template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
-
- char buff[1 << 21], *buf = buff;
-
- template <class T>
- T Fread(T sum = 0, T fg = 0)
- {
- while(*buf < &#8216;0&#8217; || *buf > &#8216;9&#8217;) { fg |= *buf == &#8216;-&#8216;; buf++; }
- while(*buf >= &#8216;0&#8217; && *buf <= &#8216;9&#8217;) { sum = sum * 10 + *buf &#8211; &#8216;0&#8217;; buf++; }
- return fg ? -sum : sum;
- }
-
- template <class T>
- T read(T sum = 0, T fg = 0)
- {
- char c = getchar();
- while(c < &#8216;0&#8217; || c > &#8216;9&#8217;) { fg |= c == &#8216;-&#8216;; c = getchar(); }
- while(c >= &#8216;0&#8217; && c <= &#8216;9&#8217;) { sum = sum * 10 + c &#8211; &#8216;0&#8217;; c = getchar(); }
- return fg ? -sum : sum;
- }
-
- const int inf = 1e9;
-
- const LL INF = 1e17;
-
- const int Size = 100010;
-
- const int maxn = 100000;
-
- const int maxm = 100000;
-
- int n, m;
-
- struct index_tree
- {
- #define lowbit(x) ((x) & (-x))
-
- void add(LL *array, int x, LL val)
- {
- for(; x <= n; x += lowbit(x)) array[x] += val;
- }
-
- LL query(LL *array, int x)
- {
- LL ans = 0;
- for(; x; x -= lowbit(x)) ans += array[x];
- return ans;
- }
- }tree;
-
- LL c1[Size], c2[Size];
-
- int main()
- {
- #ifndef ONLINE_JUDGE
- freopen(&#8220;input.in&#8221;, &#8220;r&#8221;, stdin);
- freopen(&#8220;output.out&#8221;, &#8220;w&#8221;, stdout);
- #endif
- fread(buff, 1, 1 << 21, stdin);
- n = Fread<int>(), m = Fread<int>();
- LL last_x = 0;
- REP(i, 1, n)
- {
- LL x = Fread();
- tree.add(c1, i, x &#8211; last_x);
- tree.add(c2, i, (x &#8211; last_x) * (i &#8211; 1));
- last_x = x;
- }
- while(m&#8211;)
- {
- if(Fread<int>() == 1)
- {
- int x = Fread<int>(), y = Fread<int>();
- LL val = Fread();
- tree.add(c1, x, val); tree.add(c1, y + 1, -val);
- tree.add(c2, x, val * (x &#8211; 1)); tree.add(c2, y + 1, -val * y);
- }
- else
- {
- int x = Fread<int>(), y = Fread<int>();
- LL sum1 = (x &#8211; 1) * tree.query(c1, x &#8211; 1) &#8211; tree.query(c2, x &#8211; 1);
- LL sum2 = y * tree.query(c1, y) &#8211; tree.query(c2, y);
- printf(&#8220;%lld\n&#8221;, sum2 &#8211; sum1);
- }
- }
- return 0;
- }
题目链接:https://www.luogu.org/problem/show?pid=3372
对于一个包含n个非负整数的数组a,如果有ia[j],则称(a[i],A[j])为数组a中的一个逆序对。
即逆序数就是前面比后面大的一对数。
[cpp]
view plain
copy
print
?
- /*************************************************************************
- > Author: wzw-cnyali
- > Created Time: 2017/6/13 16:59:03
- ************************************************************************/
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- #prag\
- ma GCC optimize(&#8220;O3&#8221;)
-
- using namespace std;
-
- typedef long long LL;
-
- typedef unsigned long long uLL;
-
- #define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
- #define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; &#8212; i)
- #define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
- #define debug(&#8230;) fprintf(stderr, __VA_ARGS__)
- #define mem(a, b) memset((a), b, sizeof(a))
-
- template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
- template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
-
- char buff[1 << 25], *buf = buff;
-
- template <class T>
- T Fread(T sum = 0, T fg = 0)
- {
- while(*buf < &#8216;0&#8217; || *buf > &#8216;9&#8217;) { fg |= *buf == &#8216;-&#8216;; buf++; }
- while(*buf >= &#8216;0&#8217; && *buf <= &#8216;9&#8217;) { sum = sum * 10 + *buf &#8211; &#8216;0&#8217;; buf++; }
- return fg ? -sum : sum;
- }
-
- template <class T>
- T read(T sum = 0, T fg = 0)
- {
- char c = getchar();
- while(c < &#8216;0&#8217; || c > &#8216;9&#8217;) { fg |= c == &#8216;-&#8216;; c = getchar(); }
- while(c >= &#8216;0&#8217; && c <= &#8216;9&#8217;) { sum = sum * 10 + c &#8211; &#8216;0&#8217;; c = getchar(); }
- return fg ? -sum : sum;
- }
-
- const int inf = 1e9;
-
- const LL INF = 1e17;
-
- const int Size = 100000;
-
- const int maxn = 100000;
-
- const int maxm = 100000;
-
- int n;
-
- struct node
- {
- int val, id;
- }a[Size];
-
- bool cmp(node a, node b)
- {
- return a.val < b.val;
- }
-
- struct index_tree
- {
- #define lowbit(x) ((x) & (-x))
- int tree[Size];
- void add(int x)
- {
- for(; x <= n; x += lowbit(x)) tree[x]++;
- }
-
- int query(int x)
- {
- int ans = 0;
- for(; x; x -= lowbit(x)) ans += tree[x];
- return ans;
- }
- }tree;
-
- int c[Size];
-
- int main()
- {
- #ifndef ONLINE_JUDGE
- freopen(&#8220;input.in&#8221;, &#8220;r&#8221;, stdin);
- freopen(&#8220;output.out&#8221;, &#8220;w&#8221;, stdout);
- #endif
- fread(buff, 1, 1 << 25, stdin);
- n = Fread<int>();
- REP(i, 1, n) a[i] = (node){Fread<int>(), i};
- sort(a + 1, a + n + 1, cmp);
-
- REP(i, 1, n) c[a[i].id] = i;
-
- int ans = 0;
- DREP(i, n, 1)
- {
- ans += tree.query(c[i]);
- tree.add(c[i]);
- }
- printf(&#8220;%d\n&#8221;, ans);
- return 0;
- }