1(0001) c[1]=a[1]
2(0010) c[2]=a[1]+a[2]
3(0011) c[3]=a[3]
4(0100) c[4]=a[1]+a[2]+a[3]+a[4]
5(0101) c[5]=a[5]
6(0110) c[6]=a[5]+a[6]
7(0111) c[7]=a[7]
8(1000) c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
x&-x ==> 二进制最后的1
总结公式:
c
[
i
]
=
a
[
i
−
2
k
−
1
+
1
]
+
a
[
i
−
2
k
+
2
+
.
.
.
a
[
i
]
c[i]=a[i-2^{k-1}+1]+a[i-2^k+2+...a[i]
c[i]=a[i−2k−1+1]+a[i−2k+2+...a[i]
t=6(0110)
所以 k=1
-t=-6(1010)
t&(-t)=2=2^1
所以 c[i]=a[i-lowbit(i)+1]+a[i-lowbit(i)+2]+...+a[i]
修改
+lowbit(x)
8(1000) | |||||||
---|---|---|---|---|---|---|---|
4(0100) | |||||||
2(0010) | 6(0110) | ||||||
1(0001) | 3(0011) | 5(0101) | 7(0111) |
+lowbit(3) +lowbit(4)
3(0011) =============> 4(0100) ===============> 8(1000)
区间查询求和
-lowbit(x)
-lowbit(7) -lowbit(6) -lowbit(4)
7(111)=============>6(110) =================>4(110) ===============0
时间复杂度:O(nlogn)
1.BIT-1
模板题
#include<bits/stdc++.h>
using namespace std;
long long n,m,c[1000005],k,l,r;
int lowbit(int x){
return x&(-x);
}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=y;
}
}
long long ask(int x){
long long sum=0;
for(int i=x;i;i-=lowbit(i)){
sum+=c[i];
}
return sum;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&k);
update(i,k);
}
for(int i=1;i<=m;i++){
scanf("%d",&k);
if(k==1){
scanf("%d%d",&l,&r);
update(l,r);
}
else if(k==2){
scanf("%d%d",&l,&r);
printf("%lld\n",ask(r)-ask(l-1));
}
}
return 0;
}
2.逆序对
P1908
对于给定的一段正整数序列,求一序列中有多少个逆序对。
数据大,离散化!
#include<bits/stdc++.h>
using namespace std;
int n;
map<int,int>mp;
long long c[1000005],a[1000005],b[1000005],ans;
int lowbit(int x){
return x&(-x);
}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=y;
}
}
long long ask(int x){
long long sum=0;
for(int i=x;i;i-=lowbit(i)){
sum+=c[i];
}
return sum;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
mp[a[i]]=i;
}
for(int i=1;i<=n;i++){
int x=mp[b[i]];
update(x,1);
ans+=i-ask(x);
}
printf("%lld\n",ans);
return 0;
}
树状数组进阶(关于区间修改和查询)
区间的整体修改当然得用差分,差分只需要修改头和尾两个元素就可以实现区间的整体增值或减值,也就是说接下来需要利用差分序列实现树状数组
1.差分树状数组模版题(BIT-2)
P3368
给定长度为n的序列和m次操作
m次操作为区间修改或单点查询
update改用两个节点,输入和修改共用,输入单个元素即两端点相同,采用差分数组。
#include<bits/stdc++.h>
using namespace std;
long long n,c[1000005],k,kk,kkk;
int lowbit(int x){
return x&(-x);
}
void update(int x,int y,long long z){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=z;
}
for(int i=y+1;i<=n;i+=lowbit(i)){
c[i]-=z;
}
}
long long ask(int x){
long long ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
int main(){
scanf("%lld %lld",&n,&q);
for(int i=1;i<=n;i++){
scanf("%lld",&k);
update(i,i,k);
}
while(q--){
scanf("%d",&kk);
if(kk==1){
scanf("%d %d %lld",&l,&r,&kkk);
update(l,r,kkk);
}
else{
scanf("%d",&kkk);
printf("%lld\n",ask(kkk));
}
}
return 0;
}