树状数组学习笔记
树状数组模版
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小数
题意:猫有很多,数不清,所以将它分组,一开始每只猫一组,操作为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; }