树状数组学习笔记

树状数组模版
int lowbit(int x)
 {
     return x&(-x);
 }
 
 void add(int pos,int val)////如果要把a[i]增加v,可以通过调用如下函数实现
 {
     while(pos <= MAXN)
     {
         tree[pos] += val;
         pos += lowbit(pos);//pos+lowbit(x)可以理解变成了x的父亲
     }
 }
 
 int read(int x)//前x项和
 {
     int s=0;
     while(x>0)
     {
         s += tree[x];
         x -= lowbit(x);//x-lowbit(x)可以理解成变成了x的兄弟
     }
     return s;
 }

树状数组区间求和 poj-2352-Stars

题意:一个“*”的层次是这样定义的,处于该“*”的下面和左面的范围内的“*”的个数即为该“*”的层次。题目要求处于0到n-1层次的“*”数目各有几个。

分析:由于“*”的坐标已经按Y递增的顺序排列好,对于具有相同Y坐标的“*”,又按其X坐标排序,故可充分利用这一输入特点,直接运用树状数组进行求解。

 

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 32050
using namespace std;
int tree[MAXN];
int ans[MAXN];
int lowbit(int x){
    return x&-x;
}
void add(int k,int num){
    while(k<MAXN){
        tree[k]+=num;
        k+=lowbit(k);
    }//当k=0时,会造成死循环
}
int read(int k){
    int s=0;
    while(k){
        s+=tree[k];
        k-=lowbit(k);
    }
    return s;
}
int main()
{
    int n;
    while(~scanf("%d",&n)){
        int x,y;
        memset(ans,0,sizeof(ans));
        for(int i=0;i<n;i++){
            scanf("%d%d",&x,&y);
            ans[read(x+1)]++;
            add(x+1,1);
        }
        for(int i=0;i<n;i++)printf("%d\n",ans[i]);
    }
    return 0;
}

树状数组求第k大数,相等于求第k=n-k+1小数

poj--The k-th Largest Group

题意:猫有很多,数不清,所以将它分组,一开始每只猫一组,操作为0时,表示把i,j组猫合并成新的一组,操作为1时,表示查询第k大组猫的个数

分析:猫的合并用并查集 ,动态更新第K值用树状数组。

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 200050
using namespace std;
int n,m;
int num;
int tree[MAXN],fa[MAXN];
int rank[MAXN];集合x的大小为rank[x]
int lowbit(int x){
    return x&-x;
}
void add(int pos,int num){
    while(pos<=n){
        tree[pos]+=num;
        pos+=lowbit(pos);
    }
}
int read(int p){
    int s=0;
    while(p){
        s+=tree[p];
        p-=lowbit(p);
    }
    return s;
}
int find(int x){
   if(x!=fa[x]){
    fa[x]=find(fa[x]);
   }
   return fa[x];
}//这种查询比return x==fa[x]?x:find(fa[x]);要快,如果采用注释的并查集会超时
void init(int n){
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=n;i++)rank[i]=1;
    add(1,n);//初始状态值为1的有n个,(用线段树的思想理解就是第1-r这个区间都有1个值)
}
int find_kth(int k){
    int l=1,r=n;
    while(l<=r){
        int mid=(l+r)>>1;
        if(read(mid)>=k)r=mid-1;
        else l=mid+1;
    }
    return l;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2){
//        scanf("%d%d",&n,&m);
        init(n);
        num=n;
        for(int i=0;i<m;i++){
            int cas;
            scanf("%d",&cas);
            if(!cas){
                int a,b;
                scanf("%d%d",&a,&b);
                int x=find(a);
                int y=find(b);
                if(x==y)continue;
                add(rank[x],-1);
                add(rank[y],-1);
                add(rank[y]=rank[x]+rank[y],1);
                fa[x]=y;
                num--;
            }
            else{
                int k;
                scanf("%d",&k);
                k=num-k+1;
                printf("%d\n",find_kth(k));
            }
        }
    }
    return 0;
}

 

原文地址:https://www.cnblogs.com/arbitrary/p/2635291.html