举个例子,有几个数可以通过lowbit产生0100呢?0001--lowbit-->0010-->lowbit-->0011-->lowbit-->0100,有三个数可以通过lowbit产生0100,加上它本身,总共维护了2^2个数的和。比如5(0101)没有数可以通过lowbit产生,所以维护它本身一个。6(0110)可以有5(0101)lowbit产生,所以维护两个。
大家有没有发现要通过lowbit产生X1XX(X代表未知数),只能通过X0XX+lowbit产生,而后面的XX可以是01,10,11,2^k怎么得出来该明白了吧!以下就是add(i,x)函数了
void add(int i,int x){ //第i个位置加上x
while(i <= n){
d[i] += x;
i += lowbit(i);
}
}
我们可以看到每个以Ci维护的只是一段区间和,那我们求前缀和只需找到对应的几个Ci即可。
比如我们要求前6(0110)个数的前缀和为C6 + C4。C6维护的是a[5]+a[6]。现在只需x -= lowbit(x)。0110-0010=0100。而0100永远也不可能lowbit到0110(上面说过了),所以0110和0100维护的区间是完全没有关系的。0100维护的是a[1]+a[2]+a[3]+a[4]。正好找到前缀和。
为什么这样可行呢?0110后面维护2^1个数,0100维护的是2^2个数。加起来正好是6 = 0010 + 0100。再比如11100 = 00100 + 01000 + 10000,而这些数都可以通过lowbit产生,通过维护的个数和,我们很容易能够验证这样的方法可行的。一下是求和函数。
int sum(int i){
int s = 0;
while(i > 0){
s += d[i];
i -= lowbit(i);
}
return s;
}
#include
using namespace std;
typedef long long ll;
int const N = 500000 + 10;
int d[N],n,m;
int lowbit(int x){return x&-x;}
ll sum(int i){ //区间询问
ll sum = 0;
while(i){
sum += d[i];
i -= lowbit(i);
}
return sum;
}
void add(int i,int x){ //构造线段树,在第i个位置上加x
while(i<=n){
d[i] += x;
i += lowbit(i);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a; scanf("%d",&a);
add(i,a);
}
while(m--){
int flag,x,y;
scanf("%d%d%d",&flag,&x,&y);
if(flag == 1) add(x,y);
else printf("%lld\n",sum(y) - sum(x-1));
}
}
#include
using namespace std;
int const N = 500000 + 10;
int n,m,d[N],a[N];
int lowbit(int x){return x&-x;}
void add(int i,int x){
while(i <= n){
d[i] += x;
i += lowbit(i);
}
}
int sum(int i){
int s = 0;
while(i > 0){
s += d[i];
i -= lowbit(i);
}
return s;
}
int main(){
scanf("%d%d",&n,&m);
int flag,k,x,y;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);
for(int i=1;i<=m;i++){
scanf("%d",&flag);
if(flag == 1){
scanf("%d%d%d",&x,&y,&k);
add(x,k); add(y+1,-k);
}else{
scanf("%d",&x);
printf("%d\n",sum(x));
}
}
return 0;
}
区间查询,所以维护a[1]+a[2]+……+a[n]的前缀和
a[1]+a[2]+……+a[n] = c[1]+(c[1]+c[2])+(c[1]+c[2]+c[3])+……+(c[1]+c[2]+……+c[n]) = n*(c[1]+c[2]+……+c[n])-(0*c[1]+1*c[2]+2*c[3]+……+(n-1)*c[n])
前面c[1]+c[2]+……+c[n]这一段的区间和可以维护,为了维护后面这一段,我们新建数组c2[i] = (i-1)*c[i]即可。
每次c[i]修改量为val,那么c2[i]=(i-1)(c[i]+val) = (i-1)*c[i]+(i-1)*val。所以修改偏移量为(i-1)*val
#include
using namespace std;
int const N = 200000 + 10;
typedef long long ll;
ll c1[N],c2[N],a[N];
int n,m,k;
ll lowbit(ll x){return x&-x;}
void add(ll c[N],ll i,ll x){
while(i <= n){
c[i] += x;
i += lowbit(i);
}
}
ll getsum(ll c[N],ll i){
ll s = 0;
while(i > 0){
s += c[i];
i -= lowbit(i);
}
return s;
}
int main(){
ll x,y,v;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) add(c1,i,a[i]-a[i-1]), add(c2,i,(i-1)*(a[i]-a[i-1]));
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%lld%lld",&k,&x,&y);
if(k == 1) scanf("%lld",&v), add(c1,x,v), add(c1,y+1,-v), add(c2,x,v*(x-1)), add(c2,y+1,-y*v);
else printf("%lld\n",(y*getsum(c1,y)-getsum(c2,y)) - ((x-1)*getsum(c1,x-1)-getsum(c2,x-1)));
}
}
左右括号的方法。在一个区间内种树,相当于加一对括号。用树状数组维护从起始到这个位置的左右括号的数量。区间内有左括号那么一定有这一种类型的树,只有离开了对应的右括号这种树才没有了。所以为了统计区间[x,y]内的树种类,只需把y左边左括号的个数-(x-1)左边右括号的个数即可。
#include
using namespace std;
int const N = 50000 + 10;
int c1[N],c2[N];
int n,m;
int lowbit(int x){return x&-x;}
void add(int c[N],int i){
while(i <= n){
c[i]++;
i += lowbit(i);
}
}
int getsum(int c[N],int i){
int s = 0;
while(i > 0){
s += c[i];
i -= lowbit(i);
}
return s;
}
int main(){
int k,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&k,&x,&y);
if(k == 1){
add(c1,x), add(c2,y); //c1记录左括号的个数,c2记录右括号的个数
}else{
printf("%d\n",getsum(c1,y) - getsum(c2,x-1));
}
}
}
#include
#include
#include
#include
#include
using namespace std;
int const N = 1000 + 10;
int n,m,c[N][N];
int lowbit(int x){return x&-x;}
void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
c[i][j]++;
}
int sum(int x,int y){
int s = 0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j))
s += c[i][j];
return s;
}
int main(){
int T;
scanf("%d",&T);
bool first = true;;
while(T--){
if(first) first = false;
else printf("\n");
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
char p;
scanf(" %c",&p);
if(p == 'C'){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
add(x1,y1);
add(x2+1,y1);
add(x1,y2+1);
add(x2+1,y2+1);
}else{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",sum(x,y)%2);
}
}
}
return 0;
}
这是一道树状数组求逆序数的经典题目。注意要离散化,最后要开long long。
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int const N = 500000 + 10;
int a[N],n,d[N];
ll ans;
vectorv;
int lowbit(int x){return x&-x;}
void add(int i,int x){
while(i <= n){
d[i] += x;
i += lowbit(i);
}
}
int sum(int i){
int s = 0;
while(i){
s += d[i];
i -=lowbit(i);
}
return s;
}
int main(){
while(cin>>n&&n){
v.clear();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
memset(d,0,sizeof(d));
ans = 0;
for(int i=1;i<=n;i++){
int k = lower_bound(v.begin(),v.end(),a[i]) - v.begin();
ans += (i-1-sum(++k));
add(k,1);
}
printf("%lld\n",ans);
}
return 0;
}
又是一道求逆序数的题目。先排序,x升序,x相同y升序。注意要long long,数据范围不准确。
#include
#include
#include
#include
#include
using namespace std;
int const N = 1000 + 10;
typedef long long ll;
int n,m,k,d[N];
ll ans;
struct Road
{
int x,y;
bool operator < (const Road &e) const{
return e.x == x ? y < e.y : x < e.x;
}
}road[N*N];
int lowbit(int x){return x&-x;}
void add(int i,int x){
while(i <= m){
d[i] += x;
i += lowbit(i);
}
}
int sum(int i){
int s = 0;
while(i){
s += d[i];
i -= lowbit(i);
}
return s;
}
int main(){
int T;
scanf("%d",&T);
int caser = 0;
while(T--){
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i
又是一道求逆序数的题目。题目顺序都处理好了。注意向右平移一个单位,因为0号位置也有star
#include
using namespace std;
int const N = 15000 + 10;
int const M = 32000 + 10;
int n,d[M],num[N];
int lowbit(int x){return x&-x;}
void add(int i,int x){
while(i <= M){
d[i] += x;
i += lowbit(i);
}
}
int sum(int i){
int s = 0;
while(i > 0){
s += d[i];
i -= lowbit(i);
}
return s;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){ //0好位置有
int x,y;
scanf("%d%d",&x,&y);
++x;
num[sum(x)]++;
add(x,1);
}
for(int i=0;i<=n-1;i++) printf("%d\n",num[i]);
return 0;
}