题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5592
题目大意就是给了每个[1, i]区间逆序对的个数,要求复原原序列。
比赛的时候2B了一发。
首先既然给了[1, i-1]和[1, i]区间逆序对的个数,自然可以求出与i组成逆序对的个数,自然就是i前面比i大的个数,自然也就能求出i前面比i小的个数。
然后我考虑最后一个数,由于它在最后一个,所以所有数都在它前面,自然如果知道它前面有多少个比他小的,就知道它是几了。不妨设最后一个是p,然后考虑倒数第二个,如果它前面有x个比他小的,那么倒数第二个自然是除掉p以外排在x+1位置上的那个数。以此类推。
有了这个需要数据结构的支持,可以用树状数组维护[1, n]区间1的个数,然后一旦找到某个数,就让它置0。然后找当前位置前面有x个数比他小,即在树状数组中前缀和为x+1的那个区间右值,此处我采用了二分查找。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long using namespace std; const int maxN = 50005; int n, ans[maxN]; int a[maxN]; int d[maxN]; int lowbit(int x) { return x&(-x); } void add(int id, int pls) { while(id <= maxN)//id×î´óÊÇmaxN { d[id] += pls; id += lowbit(id); } } int sum(int to) { int s = 0; while(to > 0) { s = s + d[to]; to -= lowbit(to); } return s; } void input() { memset(d, 0, sizeof(d)); scanf("%d", &n); int pre, now; pre = 0; for (int i = 1; i <= n; ++i) { scanf("%d", &now); a[i] = i-1-(now-pre); pre = now; } } int cal(int k) { int lt = 1, rt = n, mid, t; while (lt+1 < rt) { mid = (lt+rt)>>1; t = sum(mid); if (t >= k) rt = mid; else lt = mid; } if (sum(lt) == k) return lt; else return rt; } void work() { for (int i = 1; i <= n; ++i) add(i, 1); for (int i = n; i > 0; --i) { ans[i] = cal(a[i]+1); add(ans[i], -1); } for (int i = 1; i <= n; ++i) { if (i != 1) printf(" "); printf("%d", ans[i]); } printf(" "); } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 1; times <= T; ++times) { input(); work(); } return 0; }