Dr.Kong有一个容量为N MB (1 <= N <= 50,000)的存储磁盘,不妨设地址空间编号为1到N。现在他需要安装一些软件, 每个软件要占用一定大小的容量且必须装在连续的地址空间里。比如发出指令“1 100”,表示需要申请100 MB的存储空间。如果有多个满足条件的连续空间,则选择起始地址最小的一个进行分配。若没有足够的连续空间,将不安装此软件(即使有足够多的不连续存储空间)。
系统可以不定时的卸载软件,释放磁盘的空间。比如:发出“2 23 100”,表示释放起始地址为23的连续100MB大小的容量。释放时,不需要考虑这些空间里是否安装过软件。
请你编写一个程序,帮助Dr.Kong处理M (1 <= M <= 50,000)个按指令次序请求的任务。第一个请求到来前,磁盘是空的。
输入格式:
第1行: N M
第2..M+1行: 每行描述了一个请求,如果是申请,则用2个数字 1 Mi 表示;
如果是释放,则用3个数字 2 Di Mi表示。数据之间用一个空格隔开
(1<=Di ,Mi<= 50,000)
输出格式:
对于每个申请指令,输出1行。如果请求能被满足,输出满足条件的最小起始地址;如果请求无法被满足,输出0。
输入样例 :
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
输出样例 :
1
4
7
0
5
题解:
线段树维护区间最长空闲段
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=100000+10; int maxx[maxn*4],lsum[maxn*4],rsum[maxn*4]; int lr[maxn*4],tag[maxn*4]; int ql,qr; int n; inline void pushup(int o,int l,int r){ int mid=(l+r)>>1; lsum[o]=lsum[o<<1]; rsum[o]=rsum[o<<1|1]; if(rsum[o<<1|1]==r-mid&&rsum[o<<1]) rsum[o]=r-mid+rsum[o<<1]; if(lsum[o<<1]==mid-l+1&&lsum[o<<1|1]) lsum[o]=mid-l+1+lsum[o<<1|1]; if(maxx[o<<1]>=maxx[o<<1|1]){ maxx[o]=maxx[o<<1]; lr[o]=lr[o<<1]; } else { maxx[o]=maxx[o<<1|1]; lr[o]=lr[o<<1|1]; } if(rsum[o<<1]&&lsum[o<<1|1]){ int tot=rsum[o<<1]+lsum[o<<1|1]; if(tot>maxx[o]||(tot==maxx[o]&&mid-rsum[o<<1]+1<lr[o])){ maxx[o]=tot; lr[o]=mid-rsum[o<<1]+1; } } } inline void pushdown(int o,int l,int r){ if(tag[o]==-1) return ; int mid=(l+r)>>1; if(tag[o]==0){ maxx[o<<1]=lsum[o<<1]=rsum[o<<1]=0; lr[o<<1]=l; tag[o<<1]=0; maxx[o<<1|1]=lsum[o<<1|1]=rsum[o<<1|1]=0; lr[o<<1|1]=mid+1; tag[o<<1|1]=0; } else { maxx[o<<1]=lsum[o<<1]=rsum[o<<1]=mid-l+1; lr[o<<1]=l; tag[o<<1]=1; maxx[o<<1|1]=lsum[o<<1|1]=rsum[o<<1|1]=r-mid; lr[o<<1|1]=mid+1; tag[o<<1|1]=1; } tag[o]=-1; } inline void change(int o,int l,int r){ pushdown(o,l,r); if(ql<=l&&r<=qr){ maxx[o]=lsum[o]=rsum[o]=r-l+1; tag[o]=1; lr[o]=l; return ; } else if(l!=r){ int mid=(l+r)>>1; if(mid>=ql) change(o<<1,l,mid); if(mid<qr) change(o<<1|1,mid+1,r); pushup(o,l,r); } } inline void add(int o,int l,int r){ pushdown(o,l,r); if(ql<=l&&r<=qr){ maxx[o]=lsum[o]=rsum[o]=0; tag[o]=0; lr[o]=l; return ; } else if(l!=r){ int mid=(l+r)>>1; if(mid>=ql) add(o<<1,l,mid); if(mid<qr) add(o<<1|1,mid+1,r); pushup(o,l,r); } } inline void build(int o,int l,int r){ if(l==r){ maxx[o]=lsum[o]=rsum[o]=r-l+1; tag[o]=-1; lr[o]=l; return ; } else { tag[o]=-1; int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); pushup(o,l,r); } } /*inline int query(int x){ int now=1,l=1,r=n; if(maxx[now]<x) return 0x7fffffff; while(1){ int mid=(l+r)>>1; if(maxx[now<<1]>=x){ r=mid; now=now<<1; continue; } if(rsum[now<<1]&&lsum[now<<1|1]&&rsum[now<<1]+lsum[now<<1|1]>=x&&mid-rsum[now<<1]+1<lr[now]) return mid-rsum[now<<1]+1; if(maxx[now<<1|1]>=x&&lr[now<<1|1]<lr[now]){ l=mid+1; now=now<<1|1; continue; } return lr[now]; } }*/ inline int query(int o,int l,int r,int x){ pushdown(o,l,r); if(l==r&&maxx[o]<x) return 0x7fffffff; int mid=(l+r)>>1; if(maxx[o<<1]>=x) return query(o<<1,l,mid,x); if(rsum[o<<1]+lsum[o<<1|1]>=x) return mid-rsum[o<<1]+1; return query(o<<1|1,mid+1,r,x); } int main(){ freopen("haoi13t4.in","r",stdin); freopen("haoi13t4.out","w",stdout); int m; scanf("%d %d",&n,&m); int x,y,z; build(1,1,n); while(m--){ scanf("%d",&x); if(x==1){ scanf("%d",&y); int ans=query(1,1,n,y); if(ans!=0x7fffffff) printf("%d\n",ans); else puts("0"); ql=ans,qr=ans+y-1; add(1,1,n); } else { scanf("%d %d",&y,&z); ql=y; qr=y+z-1; change(1,1,n); } } return 0; } upd:注释掉的查询错误在于如果最长段与右儿子重合 并且右儿子段中有比右儿子最长段小但比查询段大 且在右儿子最长段左边 会返回右儿子最长段