题意:
插队
posi vali
编号为vali的人插在posi位置的人身后.
输出最终队伍.
/** 此题的关键在于逆序循环! 对于插队这项活动,总是越靠后越方便夺取胜利果实... 倒着循环,那么每次取的人的位置都能直接确定. 位于当前人的位置pos[i]+1,他的前面(截止到他来的时候)必然有pos[i]个人,而这pos[i]个人 一定是倒序循环尚且没有涉及的.因此要在当前人的前面留下pos[i]个位置(*). 已经被占了位置的是"未来"的事,所以对于pos[i]长的队来说并不存在.而倒着循环的时候 连续的队伍就变成了"找空位". **/ #include <cstdio> #include <cmath> #include <algorithm> using namespace std; #define REP(i,n) for(int i=0;i<(n);++i)//全部循环 #define FOR(i,l,h) for(int i=(l);i<=(h);++i)//区间升序循环 #define FORD(i,h,l) for(int i=(h);i>=(l);--i)//区间降序循环 #define lson l,m,rt<<1 // 左儿子 #define rson m+1,r,rt<<1 | 1 // 右儿子 // rt即为root,表示当前子树的根 #define N 200010 int tree[N<<2]; int val[N],pos[N],ans[N]; int id; void build(int l,int r,int rt) { tree[rt]=r-l+1;//含端点的区间内空位个数,初始化为全空 if(l==r) return; int m=(l+r)>>1; build(lson); build(rson); } void update(int p,int l,int r,int rt) { tree[rt]--;//插入了一个节点,那么一路找下来每个节点空位数都-1 if(l==r) { id=l;//如果找到了叶子节点,则将插入的位置存在id中;返回 return; } int m=(l+r)>>1;//选择中点 if(tree[rt<<1]>=p) update(p,lson);//如果左儿子的空位数足以容纳 //当前的人和他前面的所有人,则将当前人插入左儿子 else {//否则,更新右儿子.此时插入的人相对于右儿子的位置变成p-=tree[rt<<1]; //即是把左儿子的空余区间长度 p-=tree[rt<<1]; update(p,rson); } } int main(void) { int n; while(scanf("%d",&n)==1) { build(1,n,1);//从1到n建树,n为队伍长度 FOR(i,1,n) scanf("%d%d",pos+i,val+i);//存在两个数组里 FORD(i,n,1)//插在posi的后面,那么此人当前位置pos[i]+1 {///逆!!序!!循!!环!! update(pos[i]+1,1,n,1); ans[id]=val[i];//节点插入的位置id就是最终的队列位置 } FOR(i,1,n) printf("%d%c",ans[i],i==n?'\n':' '); } return 0; }