线段树
题意:1到n的线段,m个操作
Reset 将所有内存清空
New x 申请一段长度为x的最左的未被占用的区间,返回它的位置,并占用它。找不到输出Reject New
Free x 找到x单元内存所在的连续的单元块,返回这个单元块的左右端点,并且清空这个单元块,如果这个单元x根本没被占用,则输出Reject Free
Get x 找到第x个单元块(而不是第x单元),返回这个单元块的起始位置即左端点
这题还是和 poj 3667 Hotel 是一样的,查询一个最左的合法区间,修改一段区间
这题使用vector按顺序来保存单元块,方便查找(可以使用二分)
而线段树主要的query和updata函数是一样的
#include <cstdio> #include <cstring> #include <vector> using namespace std; #define lch(i) ((i)<<1) #define rch(i) ((i)<<1|1) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define N 50010 struct block{ int l,r; }; struct node{ int l,r; int mark; int tlen,llen,rlen; int mid(){ return (l+r)>>1; } int cal_len(){ return r-l+1; } void updata_len(){ tlen=llen=rlen= (mark ? 0 : cal_len()); } }; typedef struct block Block; typedef struct node node; vector <Block> B; node t[4*N]; void build(int l ,int r ,int rt) { t[rt].l = l; t[rt].r = r; t[rt].mark = 0; t[rt].updata_len(); if(l == r) return ; int mid = t[rt].mid(); int ll = lch(rt); int rr = rch(rt); build(l , mid , ll); build(mid+1 , r , rr); return ; } void updata(int l , int r , int val , int rt) { if(t[rt].l == l && t[rt].r == r) { t[rt].mark = val; t[rt].updata_len(); return ; } int mid = t[rt].mid(); int ll = lch(rt); int rr = rch(rt); if(t[rt].mark != -1) { t[ll].mark = t[rr].mark = t[rt].mark; t[rt].mark = -1; t[ll].updata_len(); t[rr].updata_len(); } if(l > mid) updata(l , r , val , rr); else if(r <= mid) updata(l , r , val , ll); else { updata(l , mid , val , ll); updata(mid+1 , r , val , rr); } t[rt].tlen = max( max(t[ll].tlen , t[rr].tlen) , t[ll].rlen+t[rr].llen); t[rt].llen = t[ll].llen; t[rt].rlen = t[rr].rlen; if(t[ll].tlen == t[ll].cal_len()) t[rt].llen += t[rr].llen; if(t[rr].tlen == t[rr].cal_len()) t[rt].rlen += t[ll].rlen; return ; } int query(int w , int rt) { if(t[rt].l == t[rt].r) { if(w == 1) return t[rt].l; else return 0; } int mid = t[rt].mid(); int ll = lch(rt); int rr = rch(rt); if(t[rt].mark != -1) { t[ll].mark = t[rr].mark = t[rt].mark; t[rt].mark = -1; t[ll].updata_len(); t[rr].updata_len(); } if(t[ll].tlen >= w) return query(w , ll); else if(t[ll].rlen + t[rr].llen >= w) return t[ll].r - t[ll].rlen + 1; else if(t[rr].tlen >= w) return query(w , rr); else return 0; } int binarysearch(int key) { int low = 0; int high = B.size() - 1; while(low <= high) { int mid = (low + high) >> 1; if(B[mid].l <= key) low = mid + 1; else high = mid - 1; } return low; //此二分查找最终返回low,二分查找要写好,否则很难查错 //B[low]代表的块是大于pos的,B[low-1]小于pos //对于添加一个新的区间,是在low这里添加,原来的low以及后面的区间往后移动 //对于删除一个区间,是low-1,low以及后面的区间往前移动 } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { B.clear(); build(1,n,1); while(m--) { char op[10]; int x,pos,index; Block tmp; scanf("%s",op); if(!strcmp(op,"Reset")) { updata(1,n,0,1); B.clear(); puts("Reset Now"); continue; } scanf("%d",&x); if(!strcmp(op,"New")) { pos = query(x,1); if(!pos) { puts("Reject New"); continue; } printf("New at %d\n",pos); tmp.l = pos; tmp.r = pos + x -1; index = binarysearch(pos); B.insert(B.begin()+index , tmp); updata(tmp.l , tmp.r , 1 , 1); } else if(!strcmp(op,"Free")) { index = binarysearch(x) - 1; if(index == -1 || B[index].r < x) { puts("Reject Free"); continue; } printf("Free from %d to %d\n",B[index].l , B[index].r); updata(B[index].l , B[index].r , 0 , 1); B.erase(B.begin()+index , B.begin()+index+1); } else //Get { if(--x >= B.size()) { puts("Reject Get"); continue; } printf("Get at %d\n",B[x].l); } } printf("\n"); } return 0; }