树状数组 2
树状数组:它能够高效地获取数组中连续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]
当我们修改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)。
下面给出树状数组的C语言实现:
//求2^k
树状数组是一个查询和修改复杂度都为log(n)级别的区间统计的数据结构,在思想上类似于线段树。
相比线段树,树状数组需要的空间较少,编程复杂度也较低,但适用范围比线段树小。
来观察一下这个图:
令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
这里有一个有趣的性质,下午推了一下发现:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + ... + An
算这个2^k有一个快捷的办法,定义一个函数如下即可:
int lowbit(int x)
{
return x&(x^(x–1));
}
利用机器补码的特点,这个函数可以改得更方便
int lowbit(int i)
{
return i&(-i);
}
如果要把a[n]增加m,可以通过调用如下函数实现
void add(int i,int v)
{
while (i<=n)
{
a[i]+=v;
i+=lowbit(i);
}
}
如果要统计a[1]到a[n]之间的和,可以通过调用如下函数实现
int sum(int i)
{
int s=0;
while (i>0)
{
s+=a[i];
i-=lowbit(i);
}
return s;
}
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)。
下面给出树状数组的C语言实现:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
//求2^k
intlowbit(intt)
{
returnt & ( t ^ ( t - 1 ) );
}
//求前n项和
intsum(intend)
{
intsum = 0;
while(end > 0)
{
sum += in[end];
end -= lowbit(end);
}
returnsum;
}
//增加某个元素的大小
voidplus(intpos,intnum)
{
while(pos <= n)
{
in[pos] += num;
pos += lowbit(pos);
}
}
|
3、扩展——二维树状数组
一维树状数组很容易扩展到二维,二维树状数组如下所示:
C[x][y] = sum(A[i][j])
其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y
|
[poj 2155 Matrix] 比如修改[8,6] 转为修改[8,1] [5,1] 如果再次修改[6,5] =》 [6 1] [4 1] [总结] 树状数组用updata操作来更新点的值。用sumup来统计某一区间的值。 { int i; for (i = x; i < maxn; i += Lowbit(i)) { tree[i] += c; } }
{ int i; int temp(0); for (i = x; i >= 1; i -= Lowbit(i)) { temp += tree[i]; } return temp; } 如果用updata里面的操作来修改某个区间的值,用sumup里面的操作得到某个点的值 { int i; for (i = x; i >0; i -= Lowbit(i)) { tree[i] += c; } }
{ int i; int temp(0); for (i = x; i <n; i += Lowbit(i)) { temp += tree[i]; } return temp; }
|
poj 1195 Mobile phones(二维树状数组)
(2011-07-23 10:44:04)
|
标签: it |
分类: ACM |
Mobile phones
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 8810 Accepted: 3906
Description
Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The squares form an S * S matrix with the rows and columns numbered from 0 to S-1. Each square contains a base station. The number of active mobile phones inside a square can change because a phone is moved from a square to another or a phone is switched on or off. At times, each base station reports the change in the number of active phones to the main base station along with the row and the column of the matrix.
Write a program, which receives these reports and answers queries about the current total number of active mobile phones in any rectangle-shaped area.
Input
The input is read from standard input as integers and the answers to the queries are written to standard output as integers. The input is encoded as follows. Each input comes on a separate line, and consists of one instruction integer and a number of parameter integers according to the following table.
The values will always be in range, so there is no need to check them. In particular, if A is negative, it can be assumed that it will not reduce the square value below zero. The indexing starts at 0, e.g. for a table of size 4 * 4, we have 0 <= X <= 3 and 0 <= Y <= 3.
Table size: 1 * 1 <= S * S <= 1024 * 1024
Cell value V at any time: 0 <= V <= 32767
Update amount: -32768 <= A <= 32767
No of instructions in input: 3 <= U <= 60002
Maximum number of phones in the whole table: M= 2^30
Output
Your program should not answer anything to lines with an instruction other than 2. If the instruction is 2, then your program is expected to answer the query by writing the answer as a single line containing a single integer to standard output.
Sample Input
0 4
1 1 2 3
2 0 0 2 2
1 1 1 2
1 1 2 -1
2 1 1 2 3
3
Sample Output
3
4
Source
IOI 2001
code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define le 1025
int pic[le][le];
int n;
int lowbit(int p){
return p&(-p);
}
void add(int x,int y,int d){
int i=y;
while(x<=n){
y=i;
while(y<=n){
pic[x][y]+=d;
y+=lowbit(y);
}
x+=lowbit(x);
}
}
int sum(int x,int y){
int i=y,s=0;
while(x>0){
y=i;
while(y>0){
s+=pic[x][y];
y-=lowbit(y);
}
x-=lowbit(x);
}
return s;
}
void init(){
int i,j;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
pic[i][j]=0;
}
int input(){
inti,X,Y,A,L,B,R,T,num;
while(scanf("%d",&i)==1){
if(i==3) return 1;
else if(i==0){
scanf("%d",&n);
init();
}
else if(i==1){
scanf("%d%d%d",&X,&Y,&A);
add(X+1,Y+1,A);
}
else{
scanf("%d%d%d%d",&L,&B,&R,&T);
num=sum(R+1,T+1);
num+=sum(L,B);
num-=sum(L,T+1);
num-=sum(R+1,B);
printf("%d\n",num);
}
}
return 0;
}
int main(void){
input();
return 0;
}
POJ 2182 lost cows
2011-09-27 20:30:07| 分类: 数据结构 | 标签: |字号大中小 订阅
有编号1到N,打乱次序。现给出一个序列,序列中每一个数代表这个位置的之前编号比它小的数的个数,找出当前位置的初始编号。算法:从后向前扫描,遇到数字a,说明它是剩余序列中第a+1个数,找到该编号后删去,重复上述操作。找的过程要借助于线段树,看一个区间内未被删除的数字个数能否满足使当前要找的数成为第a+1个,能则递归左子树,否则递归右子树,直至到叶子节点,那么叶子节点的值就是其初始编号。
=====================================================================================================
#include <stdio.h>
#include <string.h>
#define MAXN 10000
//***********************************************
typedef struct {
int left;
int right;
int rest;
}NODE;
NODE seg[3*MAXN];
int range[MAXN];
int ans[MAXN];
int N;
//***********************************************
void build(int num,int left,int right)
{
seg[num].left = left;
seg[num].right = right;
seg[num].rest = right - left + 1;
if ( seg[num].left == seg[num].right )
return;
int mid = ( left + right )/2;
build(2*num,left,mid);
build(2*num+1,mid+1,right);
}
int query(int num,int n)
{
seg[num].rest--;
if ( seg[num].left == seg[num].right )
return seg[num].left;
else if ( seg[num*2].rest >= n )
return query(num*2,n);
else
return query(num*2+1,n-seg[num*2].rest);
}
//***********************************************
int main()
{
int i;
//freopen("2182.txt","r",stdin);
scanf("%d",&N);
for (i=2; i<=N; i++)
scanf("%d",&range[i]);
range[1] = 0;
build(1,1,N);
for (i=N; i>=1; i--)
ans[i] = query(1,range[i]+1);
for (i=1; i<=N; i++)
printf("%d\n",ans[i]);
return 0;
}
题意:给定n个区间,问每个区间所覆盖的区间的个数。
思路:第三道树状数组,终于有感觉了,要不就傻X了,对区间排序,然后树状数组查询,思路和Stars那道差不多,不过还要处理重合区间的情况,只需排序后O(N)扫描一遍就好了。
/* Cows 2481
* 树状数组第三题,对牛排序即可 ,注意要
* 排除体型相等的牛.
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
using namespace std;
const int BORDER = (1<<20)-1;
const int MAXSIZE = 37;
const int MAXN = 100200;
const int INF = 1000000000;
#define CLR(x,y) memset(x,y,sizeof(x))
#define ADD(x) x=((x+1)&BORDER)
#define IN(x) scanf("%d",&x)
#define OUT(x) printf("%d\n",x)
#define MIN(m,v) (m)<(v)?(m):(v)
#define MAX(m,v) (m)>(v)?(m):(v)
#define ABS(x) ((x)>0?(x):-(x))
typedef struct{
int s,e;
int id;
}Node;
Node node[MAXN];
int tre[MAXN],n_tre;
int n;
int ans[MAXN],same[MAXN];
int lowbit(int x)
{
return x&(-x);
}
void modify(int ind,int delta)
{
while( ind <= n_tre)
{
tre[ind] += delta;
ind += lowbit(ind);
}
}
int get_sum(int ind)
{
int sum = 0;
while(ind > 0)
{
sum += tre[ind];
ind -= lowbit(ind);
}
return sum;
}
bool cmp(const Node& a,const Node& b)
{
if(a.e == b.e)
return a.s < b.s;
return a.e > b.e;
}
int init()
{
n_tre = 100400;
CLR(same,0);
CLR(tre,0);
CLR(ans,0);
return 0;
}
int input()
{
for(int i = 0; i < n; ++i)
{
scanf("%d%d",&node[i].s,&node[i].e);
++node[i].s;
++node[i].e;
node[i].id = i;
}
return 0;
}
int _find_same()
{
int i,j,tmp;
for(i = 1; i < n; ++i)
{
tmp = 0;
if(node[i-1].s == node[i].s && node[i-1].e == node[i].e)
same[i] = same[i-1] + 1;
}
return 0;
}
int work()
{
int i,j,s,e,tmp;
sort(node,node+n,cmp);
_find_same();
for(i = 0; i < n; ++i)
{
ans[node[i].id] = get_sum(node[i].s)-same[i];
modify(node[i].s,1);
}
printf("%d",ans[0]);
for(i = 1; i < n; ++i)
printf(" %d",ans[i]);
printf("\n");
return 0;
}
int main()
{
while(IN(n))
{
if(!n)
break;
init();
input();
work();
}
return 0;
}
浙公网安备 33010602011771号