Poj3667 ( 线段树题目,不过用Splay做的 )

题目链接:http://poj.org/problem?id=3667

 

题意:抽象出来就是给出一长段连续的资源,对些资源有两种操作:

 

1、申请连续的a 个资源,从前身后第一个满足的区间被分配,已被分配的资源不能重新分配,除非经操作2释放;

2、从某个位置开始,释放其后的连续a个资源,被释放的也可以是未使用的资源.

 

此题看上去有些像是计算机中内存的分配与回收算法,数据结构上有提到,可以用链表来模拟实现,可是题目给出的数据量太大了,用链表模拟的话是AC不了的,所以要考虑采用高效的数据结构来实现,那么此题的经典解法当属线段树了:

 

线段树的节点构造可以增加几个域来表示复杂的统计问题,分别是max 记录当前节点所表示的区域内最长的连续资原,ls表示从左端开始连续的资源长度,rs表示以右端结尾连续的资源长度, cov表示该段段资源以何种方式被覆盖(这个域用来提高操作效率),所以具体结构如下:

 

 

typedef struct{ int lt,rt; int max,ls,rs; int cov; }SegTree; 

然后就涉及到域的维护了,参考了Superbin学长的博客:

http://www.cnblogs.com/superbin/archive/2010/07/18/1780194.html

 

好吧,说了这么多,其实我要说的是,俺 后悔没用线段树来解这一题>.<整整两天的时间都被这一题给耗掉了,浪费啊.

 

用Splay解的:

 

思路还是使用链表那个思想,用模拟来维护资源,考虑到时间上的限制,可以用平衡树的结构来加速查询,分配及释放等操作,节点可增加一个域来记录这个节点代表的子树上最长的区间长度,结构定义如下:

typedef struct talN{ int key,max,size; talN *lt,*rt,*pr; }Node,*Tree; 

具体实现的时候我用的是Splay,虽然看到过好几个平衡树吧,但是一个都没有动手写过,从他们说的最简单的开始吧,

 

实现的时候遇到诸多问题,比如说求结点的并,找当前结点的前驱和后继等等~一一拿下,虽然时间花得多了点吧...

 

代码:(丑,很丑,丑死了><)

#include<stdio.h> #include<string.h> #include<stdlib.h> #define M 50008 typedef struct talN{ int key,max,size; talN *lt,*rt,*pr; }Node,*Tree; Tree root; Tree q[M]; int n; void Cal(Tree &cur) {//维护节点cur的max域 cur->max=cur->size; if(cur->lt&&cur->lt->max>cur->max) cur->max=cur->lt->max; if(cur->rt&&cur->rt->max>cur->max) cur->max=cur->rt->max; } void Boy(Tree &cur) {//右旋 Tree pr=cur->pr; pr->lt=cur->rt; if(cur->rt) cur->rt->pr=pr; cur->rt=pr; cur->pr=pr->pr; if(pr->pr){ if(pr==pr->pr->lt) pr->pr->lt=cur; else pr->pr->rt=cur; } cur->pr=pr->pr; pr->pr=cur; if(!cur->pr) root=cur; Cal(pr);Cal(cur); } void Girl(Tree &cur) {//左旋 Tree pr=cur->pr; pr->rt=cur->lt; if(cur->lt) cur->lt->pr=pr; cur->lt=pr;cur->pr=pr->pr; if(pr->pr){ if(pr==pr->pr->lt) pr->pr->lt=cur; else pr->pr->rt=cur; } pr->pr=cur; if(!cur->pr) root=cur; Cal(pr);Cal(cur); } void Splay(Tree &cur) {//Splay树的翻转操作 Tree pr; if(!cur) return ; while(cur->pr){ pr=cur->pr; if(pr->pr){ if(cur==pr->lt){ if(pr==pr->pr->lt) Boy(pr),Boy(cur); else Boy(cur),Girl(cur); } else{ if(pr==pr->pr->rt) Girl(pr),Girl(cur); else Girl(cur),Boy(cur); } } else{ if(cur==pr->lt) Boy(cur); else Girl(cur); } } } void Insert(int key,int size) {//插入以key开头大小为size的区间 Tree tmp,f; Tree cur=new Node; cur->lt=cur->rt=NULL;cur->key=key;cur->size=size;cur->max=size; if(root==NULL){ root=cur; cur->pr=NULL; return ; } tmp=root; while(tmp){ f=tmp; if(tmp->key>key) tmp=tmp->lt; else tmp=tmp->rt; }cur->pr=f; if(f->key>key) f->lt=cur; else f->rt=cur; Splay(cur); } Tree Del() {//从树中删除根结点,并返回它 Tree cur,tmp,ret; if(root==NULL) return NULL; ret=root; if(root->lt==NULL){ root=root->rt; if(root) root->pr=NULL; return ret; } if(root->rt==NULL){ root=root->lt; if(root) root->pr=NULL; return ret; } tmp=root->rt;cur=root->lt;cur->pr=NULL; root=cur; while(cur->rt) cur=cur->rt; Splay(cur); cur->rt=tmp;tmp->pr=cur; Cal(cur); return ret; } void Query(int size) {//查询满足条件的区间,并旋转至根 Tree tmp,cur=root; if(cur==NULL) return ; if(cur->max<size) return ; while(cur){ tmp=cur; if(cur->lt&&cur->lt->max>=size) cur=cur->lt; else if(cur->size>=size) break; else cur=cur->rt; } Splay(cur); } Tree Next(Tree cur) {//求后继结点,看起来容易,做起来还真有些麻烦呢 Tree ret; if(cur->rt==NULL){ if(!cur->pr) return NULL; ret=cur; while(ret->pr){ if(ret==ret->pr->lt) break; ret=ret->pr; } return ret->pr; } ret=cur->rt; while(ret->lt) ret=ret->lt; return ret; } Tree Pre(Tree cur) {//求前驱结点 Tree ret; if(cur->lt==NULL){ if(!cur->pr) return NULL; ret=cur; while(ret->pr){ if(ret==ret->pr->rt) break; ret=ret->pr; } return ret->pr; } ret=cur->lt; while(ret->rt) ret=ret->rt; return ret; } void Rlease(int a,int b) {//释放资源 Tree cur,tmp,del; int head,tail,x,y; if(root==NULL) {Insert(a,b);return ;} cur=root; while(cur){ tmp=cur; if(cur->key>=a) cur=cur->lt; else cur=cur->rt; } x=a;y=a+b;head=tail=0; cur=tmp; //区间合并啊,花了一个晚上,真是的,脑子都给长长的代码搅浑了 while(cur&&cur->key+cur->size>=x){ tmp=cur; cur=Pre(cur); } while(tmp&&tmp->key<=y){ q[tail++]=tmp; tmp=Next(tmp); } //删除被覆盖的结点,然后插入新的并结点 while(head!=tail){ cur=q[head++]; if(cur->key>=x&&cur->key<=y ||(cur->key+cur->size)>=x&& (cur->key+cur->size)<=y|| cur->key<=x&&cur->key+cur->size>=y){ if(cur->key<a){ b+=a-cur->key; a=cur->key; } if(cur->key+cur->size>y) b=cur->key+cur->size-a; Splay(cur); del=Del(); free(del); } } Insert(a,b); } void Destroy(Tree cur) {//释放树 if(cur==NULL) return ; Destroy(cur->lt); Destroy(cur->rt); free(cur); } void Travers(Tree cur) {//层序遍历输出树结构 int head,tail; head=tail=0; if(cur==NULL) return; q[tail++]=cur; while(head!=tail){ cur=q[head++]; printf("(%d %d)/n",cur->key,cur->size); if(cur->lt) q[tail++]=cur->lt; if(cur->rt) q[tail++]=cur->rt; } return ; } int main() { int n,m,op,a,b; Tree cur; //freopen("hotel.in","r",stdin); //freopen("tempt.out","w",stdout); //int t=clock(); while(scanf("%d%d",&n,&m)!=EOF){ root=NULL; Insert(1,n); while(m--){ scanf("%d",&op); if(op==1){ scanf("%d",&b); Query(b); if(root&&root->size>=b){ printf("%d/n",root->key); cur=Del(); cur->key+=b; cur->size-=b; if(cur->size) Insert(cur->key,cur->size); free(cur); } else puts("0"); } else{ scanf("%d%d",&b,&a); Rlease(b,a); } //Travers(root); } Destroy(root); } //printf("%d/n",clock()-t); return 0; }  

 

你可能感兴趣的:(数据结构,struct,tree,null,query,insert)