扯淡:
这题要赞。
因为卡了我两次……
第三次终于做出来了。。。好吧是看了别人代码之后T T。太弱太弱。。。
题意:
有n个人排队,但他们会插队。他们按先后顺序来到,每次每个人选择插在Pos[i]这个位置,问最后的排队情况(每个人用其val值表示)。(1 ≤ N ≤ 200,000)
思路:
容易想到,最后一个人,插哪,那它就在哪。
也就是说,一个人的最后位置,只会被它后面的人影响到。
再分析,就会发现,如果一个人插在Pos[i]这个位置,那么表示,它插入的时候,前面有Pos[i]个人(废话)。那么我们倒着计算的时候,由于他后面的人已经进来了,那么第i个人站的实际位置,是在它前面还有pos[i]个空位的位置(加上它本身这个空位一共是pos[i]+1个)
这里,粗暴的是想用二分+线段树查找的算法,这样是logNlogN,实际上可以直接在线段树上完成。这点很赞!!!!!
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #define N 200010 int tree[N<<2]; int pos[N], a[N], val[N]; void build() { memset(tree,0,sizeof(tree)); } //int query(int L, int R, int l, int r, int rt) { // if (L <= l && r <= R) { // return tree[rt]; // } // int mid = (l+r)/2; // int ans = 0; // if (L <= mid) ans += query(L, R, l, mid, rt<<1); // if (R > mid) ans += query(L, R, mid+1, r, rt<<1|1); // return ans; //} void insert(int x, int v, int l, int r, int rt) { //printf("[%d,%d] rt = %d ", l, r, rt); if (l == r) { tree[rt]++; a[l] = v; return ; } int mid = (l+r)/2; if (x <= (mid-l+1-tree[rt<<1])) insert(x,v,l,mid,rt<<1); else insert(x-(mid-l+1-tree[rt<<1]),v,mid+1,r,rt<<1|1); tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } int main() { int n; while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; i++) { scanf("%d%d", &pos[i], &val[i]); } build(); for (int i = n-1; i >= 0; i--) { insert(pos[i]+1,val[i],0,n,1); } for (int i = 0; i < n; i++) { printf(i==0?"%d":" %d", a[i]); }puts(""); } return 0; }