题意:n 个人排队,接下来 n 行每行第一个数是这个人的位置,第二个数是他的 value(没卵用)。后来的人如果他的位置已经有人的话,所有他后面的人都向后移动 1,也就是插队的感觉。求最后这些人的顺序。
最后一个人的位置肯定不会变,因为是最后插入的,所以后来的人的位置只受前面的人影响,因此从后往前考虑的话,第 i 个人的位置就是第 pos+1 个空位。将树状数组初始化为 1,来人的话-1,用 sumseek 函数找到插入位置。
//寻找前缀和为k的第一个位置 int sumseek(int k){ int ans = 0, sum = 0, i; for(i = 18; i >= 0; i--){ ans += (1 << i); if (ans >= n || sum + a[ans] >= k) ans -= (1 << i); else sum += a[ans]; } return ans+1; }
第一次知道树状数组还可以这么玩。。
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> using namespace std; int n; struct node{ int pos, num; }N[200005]; int a[200005]; int ans[200005]; int lowbit(int x){ return x&(-x); } void add(int pos, int num){ while(pos <= n){ a[pos] += num; pos += lowbit(pos); } } int sum(int n){ int sum = 0; while(n > 0){ sum += a[n]; n -= lowbit(n); } return sum; } int sumseek(int k){ int ans = 0, sum = 0, i; for(i = 18; i >= 0; i--){ ans += (1 << i); if (ans >= n || sum + a[ans] >= k) ans -= (1 << i); else sum += a[ans]; } return ans+1; } int main(){ //freopen("a.txt", "r", stdin); while(~scanf("%d", &n)){ //memset(a, 0, sizeof(a)); for(int i = 1; i <= n; i++){ int w; scanf("%d %d", &w, &N[i].num); N[i].pos = w+1; add(i, 1); } for(int i = n; i >= 1; i--){ int k = sumseek(N[i].pos); ans[k] =N[i].num; add(k, -1); } for(int i = 1; i <= n; i++){ cout << ans[i]; if(i!=n) cout << " "; } cout << endl; } return 0; }
如果不知道 sumseek 函数的话,只能二分找到插入的位置了。