清点人数(jdoj1427-vijos1320)
题目大意:n个点,k个事件。每个事件为A , B , C。A表示查询该点的前缀和,B点是该点加权值,C点是该点减权值。
注释:n<=500000,k<=100000,至少有30000个A
想法:显然,单点修改,区间查询,数据范围较大,想到用一个较强的数据架构——线段树维护。而今天,我们来介绍一种特殊的数据结构——树状数组。
接下来,我们来介绍树状数组的性质即特性。我们想线段树一样来理解树状数组。我们用 a[ ]来记录一段区间,A[]表示单点的权值。我们用一种特殊的想法——a[1]=A[1],a[2] = A [ 1 ] + A [ 2 ],a[3]=A[3] , a[4]=a[2]+a[3]+A[4].....那么,我们再次介绍一种位运算——lowbit,表示一个数的二进制表示中从末位开始的第一个非零数的位置的值,lowbit返回一个二的r次方 , r 便是那个值。由这个东西,我们就构架出整个树状数组的体系——一切的修改之间的差值为上一个值的lowbit,而lowbit可以简单的推出,为t&(-t),即,它表示的就是按位,取反,加一的全过程。这样,查询啊,修改啊什么的就全部可以在O(logn)的时间复杂度内实现。那么,我们发现,可以用树状数组实现的,线段树都可以做到,但,为什么我们仍然如此推崇树状数组?原因十分简单,好写!太jb好写了。比那个什么线段树的代码量小到不知道哪里去了。所以在此,极其推荐树状数组,而且比较好理解。
我们回归这道题,我们想,每次单点修改,只会对后面产生贡献,所以,我们就可以对其后面的shu进行lowbit差值的修改,注意数据量,细心即可。
最后,附上丑陋的代码......
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n; 5 char a[5]; 6 int tree[500001]; 7 int lowbit(int t) 8 { 9 return t&(-t); 10 } 11 void add(int x,int y) 12 { 13 for(int i=x;i<=n;i+=lowbit(i)) 14 { 15 tree[i]+=y; 16 } 17 } 18 void query(int x) 19 { 20 int ans=0; 21 for(int i=x;i;i-=lowbit(i)) 22 { 23 ans+=tree[i]; 24 } 25 printf("%d ",ans); 26 } 27 int main() 28 { 29 int m; 30 int x,y; 31 scanf("%d%d",&n,&m); 32 for(int i=1;i<=m;i++) 33 { 34 scanf("%s",a+1); 35 if(a[1]=='A') 36 { 37 scanf("%d",&x); 38 query(x); 39 } 40 else if(a[1]=='B') 41 { 42 scanf("%d%d",&x,&y); 43 add(x,y); 44 } 45 else 46 { 47 scanf("%d%d",&x,&y); 48 add(x,-y); 49 } 50 } 51 return 0; 52 }
小结:错误
1.记住,这时一个教训:lowbit函数完全可以写在for循环里面,没有必要单独列出,导致效率变低。
2.printf与%c千万别在一起使!!