树状数组,入门总结~~

    线段树是一个很好的维护区间关系的这样的一个数据结构,但是,很多时候我们可以用更小空间、更快速度的数据结构来维护一个前缀关系
   树状数组详解:数据结构——树状数组篇
   顺便借张图:c数组所代表的范围都在图上了
在这里插入图片描述
树状数组的主角:x的二进制表达式中最低位的1所对应的值,比如0110对应的是0010,它除了x最低
的1的位置上是1,其余位上都是0,两种求法:

#define lowbit(x) ( x&(-x) )
#define lowbit(x) (x - (x & (x - 1)))

还是 \color{red}{以维护区间和为例} 说基本操作吧
//更新和查询的时候操作相反,一个一直加lowbit(x),一个一直减lowbit(x)
更新: O ( l o g n ) O(logn)

手动模拟的修改相应值要更新的序号
1(0001)—— A[1]、A[2]、A[4]、A[8];
2(0010)—— A[2]、A[4]、A[8];
3(0011)—— A[3]、A[4]、A[8];
4(0100)—— A[4]、A[8];
5(0101)—— A[5]、A[6]、A[8];
6(0110)—— A[6]、A[8];
7(0111)—— A[7]、A[8];
8(1000)—— A[8];

有点像线段树里面往上回溯,修改上面的值,树状数组是一直回溯到最大的那个值

void add(int x,int val)//第x为给它加上val的权
{
     //预防x=0的时候出现死循环,还有后面也要记得加上c[0]
     //更多的时候习惯给所有进入更新操作的数都加上1,就不用担心出现0了,这时候N也要加1
     if(!x)
     {
          c[x]+=val;
          return;
     }
     while(x<=N)//N是边界值
     {
          c[x]+=val;
          x+=x&(-x);
     }
}

查询: O ( l o g x ) O(logx)

手动模拟的修改相应值要更新的序号
1 = (0001)—— Q[1] = A[1];
2 = (0010)—— Q[2] = A[2];
3 = (0011)—— Q[3] = A[3] + A[2];
4 = (0100)—— Q[4] = A[4];
5 = (0101)—— Q[5] = A[5] + A[4];
6 = (0110)—— Q[6] = A[6] + A[4];
7 = (0111)—— Q[7] = A[7] + A[6] + A[4];
8 = (1000)—— Q[8] = A[8];

凭直觉可以得出(找规律),一直减lowbit(x)就可以了

LL query(int x)
{
     LL ans=c[0];
     //如果没有c[0]的话,直接赋为0就行了
     while(x)
     {
          ans+=c[x];
          x^=x&(-x);
     }
     return ans;
}

基本操作就这两个了,注意:
1、注意0,会造成更新操作中的死循环
2、N的值可以比边界值大,但不能小,如果更新操作中数的值都加上了1,N也要加1
3、之前做题犯的一个小错误:查询操作中,都知道如果要求前x个数的和,就要从c[x]开始加,比如前7个数的和=c[7]+c[6]+c[4],但是如果因为题目要求不同不需要第x位上的数据,这时候不是不加c[x]就好了,而是要让x先减1,这样算前x-1个数的和

例1(一个树状数组的入门题):POJ 2352 Stars
感觉题目的引导挺好的,每个左边都是按一定顺序输入的,这样就可以保证后面星星的level就是之前出现过的星星中,横坐标x小于等于i星横坐标的那些星星的总数量。
不过最开始我想半天还是没有想出了怎么解,,,,,,,,,

再看几个题感受一下树状数组的入门套路(难度顺序差不多也是这样):
例2:POJ 2481 Cows

例3:POJ 3067 Japan
(不知道为什么两个城市之间铁路也可以建两条)

例4:HDU 1540 Tunnel Warfare
(也可以用STL做,stack和set)

例5:POJ 1990 MooFest

例6:POJ 2299 Ultra-QuickSort
(树状数组求逆序数,离散化

例7:POJ 3416——Crossing
(发现有时候也可以写几个树状数组)

感觉一般都是两个变量,将一个变量进行一些操作,变成为对求解有利的条件,就像星星那个题里面y升序就保证了,后面比当前x小的数量和就是星星的level
例2代码:

#include <iostream>
#include <string.h>
#include<stdio.h>
#include<string>
#include<algorithm>
typedef long long LL;
#define N 20000
using namespace std;
const int manx=2e5+10;
const int INF=0x3f3f3f3f;
int n,c[manx];
struct node
{
    int l,r,pos,num;
} cow[manx];
int cmp(node a,node b)
{
    if(a.l==b.l)
        return a.r>b.r;
    return a.l<b.l;
}
int cmp1(node a,node b)
{
    return a.pos<b.pos;
}
void add(int x)
{
    while(x<=100001)
    {
        c[x]++;
        x+=x&(-x);
    }
}
int query(int x)
{//比小的数,不是不加c[x]就行了
    x--;
    int ans=0;
    while(x)
    {
        ans+=c[x];
        x^=x&(-x);
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n),n)
    {
        memset(c,0,sizeof(c));
        for(int i=0; i<n; i++)
        {
            cow[i].pos=i;
            scanf("%d%d",&cow[i].l,&cow[i].r);
            cow[i].l++;
            cow[i].r++;
        }
        sort(cow,cow+n,cmp);
        add(cow[0].r);
        cow[0].num=0;
        for(int i=1; i<n; i++)
        {
            if(cow[i].l==cow[i-1].l&&cow[i].r==cow[i-1].r)
                cow[i].num=cow[i-1].num;
            else
                cow[i].num=i-query(cow[i].r);
            add(cow[i].r);
        }
        sort(cow,cow+n,cmp1);
        for(int i=0; i<n-1; i++)
            printf("%d ",cow[i].num);
        printf("%d\n",cow[n-1].num);
    }
}
发布了52 篇原创文章 · 获赞 26 · 访问量 3183

猜你喜欢

转载自blog.csdn.net/qq_43803508/article/details/97761260