题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5493
题目大意:一个队列中有n个身高不同的人,现在他们吃饭回来要站回原来的队列,每个人只记得他前面或后面有k个比他高的人,现在给你每个人的身高,要还原出一个字典序最小的队列。
分析:和POJ2828 Buy Tickets有点相似,做法大体相同,我们先把这n个人按身高升序排序(定义结构体p存放地每个人的k和h),对于第i个人,如果他看到的比他高的人的人数大于实际比他高的人的人数(即p[i].k>n-i),那么显然是无法还原的;否则,他在队列中的位置就是第p[i].k+1(他前面有k个比他高的)或者n-i-p[i]+1(他后面有k个比他高的)个,由于要还原出字典序最小的一个,我们选择两者之间的较小值作为他在队列中的位置。实现上,我们可以用线段树来纪录每个区间的空位置数,用叶子节点来纪录队列中第i个人的身高。
实现代码如下:
#include <cstdio> #include <algorithm> using namespace std; const int N=1e5+10; struct person { int h,k; bool operator <(const person &a) const { return h<a.h; } }p[N]; struct segment { int l,r; int emp; int mid() { return (l+r)>>1; } }tree[N<<2]; int heigh[N]; void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; tree[rt].emp=r-l+1; if(l==r) return ; int m=tree[rt].mid(); build(rt<<1,l,m); build(rt<<1|1,m+1,r); } void update(int rt,int l,int r,int h,int k) { if(tree[rt].l==tree[rt].r) { tree[rt].emp=0; heigh[l]=h; return ; } int m=tree[rt].mid(); if(k<=tree[rt<<1].emp) update(rt<<1,l,m,h,k); else update(rt<<1|1,m+1,r,h,k-tree[rt<<1].emp); tree[rt].emp=tree[rt<<1].emp+tree[rt<<1|1].emp; } int min(int a,int b) { return a<b?a:b; } int main() { int t,T=1,n; scanf("%d",&t); while(t--) { scanf("%d",&n); build(1,1,n); for(int i=1;i<=n;i++) scanf("%d%d",&p[i].h,&p[i].k); sort(p+1,p+1+n); bool flag=true; for(int i=1;i<=n;i++) { if(p[i].k>n-i) { flag=false; break; } int k=min(p[i].k,n-i-p[i].k); update(1,1,n,p[i].h,k+1); } printf("Case #%d:",T++); if(!flag) { puts(" impossible"); continue; } for(int i=1;i<=n;i++) printf(" %d",heigh[i]); printf("\n"); } return 0; }