前几天学习了划分树和SBT,一直没机会训练一下,今天实验室有网了,就训练POJ上面一道水题。分别用两种数据结构来实现了一下!
这道题题目很简单,就是给定序列,求区间最小值!
SBT:
- #include<stdio.h>
- #include<algorithm>
- using namespace std;
- #define M 100005
- struct SBT{
- int key,left,right,size;
- }tree[M];
- int root,top;
- int a[M];
- struct Query{
- int ql,qr,k,id,ans;
- }que[M/2];
- bool cmp(const Query &q1,const Query &q2){
- if(q1.ql==q2.ql){
- return q1.qr<q2.qr;
- }
- return q1.ql<q2.ql;
- }
- bool cmp2(const Query &q1,const Query &q2){
- return q1.id<q2.id;
- }
- void init(){
- root=top=0;
- }
- //这两种旋转看起来是挺容易的,但自己写容易混乱,最好能结合图
- void rotate_left(int &x){
- int y=tree[x].right;
- tree[x].right=tree[y].left;
- tree[y].left=x;
- tree[y].size=tree[x].size;
- tree[x].size=tree[tree[x].left].size+tree[tree[x].right].size+1;
- x=y;
- }
- void rotate_right(int &x){
- int y=tree[x].left;
- tree[x].left=tree[y].right;
- tree[y].right=x;
- tree[y].size=tree[x].size;
- tree[x].size=tree[tree[x].left].size+tree[tree[x].right].size+1;
- x=y;
- }
- void maintain(int &x,bool flag){
- if(flag==false){//数据插入到左边了
- if(tree[tree[tree[x].left].left].size>tree[tree[x].right].size){//情况 1
- //左孩子的左子树大于右孩子
- rotate_right(x);
- }else if(tree[tree[tree[x].left].right].size>tree[tree[x].right].size){ //情况 2
- rotate_left(tree[x].left);//先把树旋转成: 情况 1
- rotate_right(x);//按照情况1进行旋转
- }else return ;
- }else{//数据插入到右边了
- if(tree[tree[tree[x].right].right].size>tree[tree[x].left].size){
- rotate_left(x);
- }else if(tree[tree[tree[x].right].left].size>tree[tree[x].left].size){
- rotate_right(tree[x].right);
- rotate_left(x);
- }else return ;
- }
- maintain(tree[x].left,false);
- maintain(tree[x].right,true);
- maintain(x,false);
- maintain(x,true);
- }
- /*
- * 这里用数组实现,递归插入,挺巧妙的,我自己用数组做的时候都不会想到这样做
- * tree的0位置空着不用; 帅气,如果不进行调整的话,root能保持root=1
- * */
- void insert(int &x,int key){
- if(x==0){
- x=++top;//确定插入的数组下标
- tree[x].left=tree[x].right=0;
- tree[x].key=key;
- tree[x].size=1;
- }else{
- tree[x].size++;
- if(key<=tree[x].key)//把相同的元素插入到左边
- insert(tree[x].left,key);
- else insert(tree[x].right,key);
- maintain(x,key>tree[x].key);
- }
- }
- //这里要保证删除的数据存在
- void remove(int &x,int key){
- tree[x].size--;
- if(key>tree[x].key){
- remove(tree[x].right,key);
- }else if(key<tree[x].key){
- remove(tree[x].left,key);
- }else{
- //有左子树,无右子树
- if(tree[x].left!=0&&tree[x].right==0){
- x=tree[x].left;
- //无左子树,有右子树
- }else if(tree[x].right!=0&&tree[x].left==0){
- x=tree[x].right;
- //叶子结点
- }else if(tree[x].left==0&&tree[x].right==0){
- x=0;
- //有左子树,也有右子树
- }else{
- int temp=tree[x].right;
- while(tree[temp].left)temp=tree[temp].left;
- tree[x].key=tree[temp].key;
- remove(tree[x].right,tree[temp].key);
- }
- }
- }
- //求第k小的数
- int select(int &x,int k){
- int r=tree[tree[x].left].size+1;
- if(r==k)return tree[x].key;
- else if(r<k)return select(tree[x].right,k-r);
- else return select(tree[x].left,k);
- }
- int main(){
- int n,m;
- int i,j;
- init();
- scanf("%d %d",&n,&m);
- for(i=1;i<=n;i++){
- scanf("%d",&a[i]);
- }
- int ql,qr,k;
- for(i=0;i<m;i++){
- scanf("%d %d %d",&ql,&qr,&k);
- que[i].id=i;
- que[i].k=k;
- que[i].ql=ql;
- que[i].qr=qr;
- }
- sort(que,que+m,cmp);
- for(i=que[0].ql;i<=que[0].qr;i++){
- insert(root,a[i]);
- }
- que[0].ans=select(root,que[0].k);
- for(i=1;i<m;i++){
- //前一个区间与当前区间只有两种关系,相交或者不相交
- if(que[i].ql<=que[i-1].qr){
- //看起点
- if(que[i].ql>que[i-1].ql){
- for(j=que[i-1].ql;j<que[i].ql;j++){
- remove(root,a[j]);
- }
- }
- //看终点
- if(que[i].qr<que[i-1].qr){//
- for(j=que[i].qr+1;j<=que[i-1].qr;j++){
- remove(root,a[j]);
- }
- }else if(que[i].qr>que[i-1].qr){
- for(j=que[i-1].qr+1;j<=que[i].qr;j++){
- insert(root,a[j]);
- }
- }
- }else{
- for(j=que[i-1].ql;j<=que[i-1].qr;j++){
- remove(root,a[j]);
- }
- for(j=que[i].ql;j<=que[i].qr;j++){
- insert(root,a[j]);
- }
- }
- que[i].ans=select(root,que[i].k);
- }
- sort(que,que+m,cmp2);
- for(i=0;i<m;i++){
- printf("%d\n",que[i].ans);
- }
- return 0;
- }
划分树:
- #include<stdio.h>
- #include<algorithm>
- using namespace std;
- #define M 100005
- int tree[30][M];
- int toLeft[30][M];
- int sorted[M];
- int n,m;
- void build(int level,int left,int right){
- if(left==right)return;
- int mid=(left+right)>>1;
- int suppose;
- int i;
- suppose=mid-left+1;
- for(i=left;i<=right;i++){
- if(tree[level][i]<sorted[mid]){
- suppose--;
- }
- }
- int lpos=left,rpos=mid+1;
- for(i=left;i<=right;i++){
- if(i==left){
- toLeft[level][i]=0;
- }else{
- toLeft[level][i]=toLeft[level][i-1];
- }
- if(tree[level][i]<sorted[mid]){
- toLeft[level][i]++;
- tree[level+1][lpos++]=tree[level][i];
- }else if(tree[level][i]>sorted[mid]){
- tree[level+1][rpos++]=tree[level][i];
- }else{
- if(suppose){
- suppose--;
- toLeft[level][i]++;
- tree[level+1][lpos++]=tree[level][i];
- }else{
- tree[level+1][rpos++]=tree[level][i];
- }
- }
- }
- build(level+1,left,mid);
- build(level+1,mid+1,right);
- }
- int query(int level,int left,int right,int ql,int qr,int k){
- if(ql==qr){
- return tree[level][ql];
- }
- int s,ss;
- int mid=(left+right)>>1;
- if(left==ql){
- s=0;
- ss=toLeft[level][qr];
- }else{
- s=toLeft[level][ql-1];
- ss=toLeft[level][qr]-s;
- }
- int nql,nqr;
- if(k<=ss){
- nql=left+s;
- nqr=left+s+ss-1;
- return query(level+1,left,mid,nql,nqr,k);
- }else{
- nql=mid-left+1+ql-s;
- nqr=mid-left+1+qr-s-ss;
- return query(level+1,mid+1,right,nql,nqr,k-ss);
- }
- return 0;
- }
- int main(){
- scanf("%d %d",&n,&m);
- for(int i=1;i<=n;i++){
- scanf("%d",&sorted[i]);
- tree[0][i]=sorted[i];
- }
- sort(sorted+1,sorted+n+1);
- build(0,1,n);
- int ql,qr,k;
- for(int i=0;i<m;i++){
- scanf("%d %d %d",&ql,&qr,&k);
- int ans=query(0,1,n,ql,qr,k);
- printf("%d\n",ans);
- }
- return 0;
- }