题目链接:
http://poj.org/problem?id=2828
题目意思:
有n个人排队,每个人有个pos值,和value值,表示他可以插到第pos个人的后面,输出最后的队形序列。
解题思路:
如果顺着插的话,要移动后面的队列,如果用链表的话,找到插入的位置很费时。
如果逆着考虑的话,当前的人插到恰好有pos个空位的最小的地方,就很简单。
用线段树维护区间内空位的个数,如果要求的空位数大于左边区间的空位数,则减去左边的空位数,再在右边查找。
总结:抽象出线段树维护的空间很重要。
代码:
//如果逆着推的话,就不用移动很多元素了,这是关键, //用线段树维护区间的空位数,如果左边的空位数不够的话,选择右边(其中把左边占满),知道找到插入的位置 //线段树 抽象出维护的空间 很关键 #include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF (1<<30) #define PI acos(-1.0) using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 210000 int sum[maxn*4]; int pos[maxn],value[maxn],ans[maxn],n; int temp; /* freopen("data.in","r",stdin); freopen("data.out","w",stdout); */ void build(int l,int r,int rt) { sum[rt]=r-l+1; //初始区间空位的多少 if(r==l) return ; int m=(l+r)>>1; build(lson); build(rson); } void update(int target,int l,int r,int rt) { sum[rt]--; //所到之处,空位数减一,因为必须在某个区间插 if(l==r) { temp=l; //找到空位的位置,记住然后进行插入 return ; } int m=(l+r)>>1; if(target<=sum[rt<<1]) //如果左区间的空位数够的话,在左区间更新 update(target,lson); else update(target-sum[rt<<1],rson); //左区间不够,先把左区间占满,然后在填右区间 return ; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d%d",&pos[i],&value[i]); build(1,n,1); for(int i=n;i>=1;i--) { update(pos[i]+1,1,n,1); ans[temp]=value[i]; //把位置找到然后插入 } printf("%d",ans[1]); for(int i=2;i<=n;i++) printf(" %d",ans[i]); putchar('\n'); } return 0; }