二进制操作和树状数组
RoderickQiu
2019-03-12 17:46:50
# 二进制运算符
- `a&b` 按位与(二进制中的每一位都执行与运算,如`01010101`和`10111011`,结果为00010001)
- `a|b` 按位或(二进制中的每一位都执行或运算)
- `a>>n` 右移运算(二进制的每一位都向右移动n位,相当于`a/(2^n)`)
- `a<<n` 左移运算(二进制的每一位都向左移动n位,相当于`a*(2^n)`)(谨防数位溢出)
# 二进制运算技巧
- `a&1` 把a和1按位与,则a为单数时值为1,为双数时值为0。
- `a&-a` 把a和-a按位与,可求出a的最低位1,也就是lowbit。
以下给出原理:
_我们知道一个数加一个负号是把这个数的二进制取反+1,_
_负数用在计算机中用补码表示_
_负数的反码 是其正数的原码 取反_
_负数的补码 是 其正数的原码取反 + 1_
_[10]原码 = 1010_
_[-10]反码 = 0101_
_[-10]补码 = 0110_
_如-10的二进制就是-1010=0101+1=0110,_
_然后用1010&0110,_
_答案就是0010了!_
# 树状数组
先上代码。
```cpp
#include <bits/stdc++.h>
using namespace std;
int n,m,t,x,y,j,sum;
int a[500005],tree[500005];
int lowbit(int i) {//lowbit:获得最低的一个1
return i&-i;//i&-i,记住就行
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
j=i;
while(j<=n) {
tree[j]+=a[i];//每个节点都添加当前的值,预处理
j+=lowbit(j);//进入下一个“更高节点级”的节点
}
}
for(int i=0; i<m; i++) {
scanf("%d %d %d",&t,&x,&y);
if(t==1) {
j=x;
while(j<=n) {
tree[j]+=y;
j+=lowbit(j);
}//加法操作,和初始化操作同理
} else {
sum=0;
while(y) {
sum+=tree[y];
y-=lowbit(y);
}//获取1-y
x-=1;
while(x) {
sum-=tree[x];
x-=lowbit(x);
}//获取1-x,并执行(1+...+y)-(1+...+x)
cout<<sum<<endl;
}
}
return 0;
}
```
原理如下: _(图片来自于网上,来源已不可考)_
![原理](https://cdn.luogu.com.cn/upload/pic/53873.png)