题目链接:http://poj.org/problem?id=2828
题目大意:给定n个人进入队伍时的位置,如果某个位置及其后面有人,则后面的人都要向后挪一个位置,这不正是每天都在现实生活中上演的插队问题吗!有n个pos[i]和val[i],pos[i]表示这个人插入到pos[i]的右面,其实加1的话,更好理解,就是插在什么位置。至于那个val[i]只是为了表示某个人而已,理解成RP什么的都可以。
解题思路:这题用线段树解。现在我们按照题目的意思模拟,一遍,如果有人插入进来,则要挪动已经排好的队伍,这样复杂度是(n^2),太可怕了,拒绝暴力。换个角度,后插进来的人更容易定位,比如最后一个人插在什么位置就是在什么位置,不需要挪动,这也很容易理解。现在从后面开始定位每个人的位置,不失一般性,如果某个人插入到pos[i],则找第pos[i]个空位置插入即可,记录空位置可以通过线段树实现。具体实现见代码
测试数据:
4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492
4
0 20523
1 19243
1 3890
1 31492
代码:#include <stdio.h> #include <string.h> #define MAX 200010 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int empty[MAX<<2],index,n; int pos[MAX],val[MAX],ans[MAX]; void build(int l,int r,int rt){ empty[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){ empty[rt]--; //单点更新操作,减少一个空位 if (l == r) { index = l; return; } int m = (l + r) >> 1; if (empty[rt<<1] >= p) //如果当前位置的左边空位够的话最终落在左边 update(p,lson); else //否则,就把左边的空位减去,再在右边定位 p -= empty[rt<<1],update(p,rson); } int main() { int i,j,k; while (scanf("%d",&n) != EOF){ build(1,n,1); for (i = 1; i <= n; ++i) scanf("%d%d",&pos[i],&val[i]); for (i = n; i >= 1; --i) update(pos[i]+1,1,n,1),ans[index] = val[i];//pos[i]+1,就是准确的空位 for (i = 1; i <= n; ++i) //如果没有+1在,线段树判断左右子树时只判小于就可以 printf(i == n ? "%d\n" : "%d ",ans[i]); } }