ZYB有一个排列P,但他只记得P中每个前缀区间的逆序对数,现在他要求你还原这个排列. (i,j)(i<j)被称为一对逆序对当且仅当Ai>Aj
输入描述
第一行一个整数T表示数据组数。 接下来每组数据: 第一行一个正整数N,描述排列的长度. 第二行N个正整数Ai,描述前缀区间[1,i]的逆序对数. 数据保证合法. 1≤T≤5,1≤N≤50000
输出描述
T行每行N个整数表示答案的排列.
输入样例
1 3 0 1 2
输出样例
3 1 2
设fi是第i个前缀的逆序对数,pi是第i个位置上的数,则fi−fi−1是i前面比pi大的数的个数.我们考虑倒着做,当我们处理完i后面的数,第i个数就是剩下的数中第fi−fi−1+1大的数,用线段树和树状数组可以轻松地求出当前第k个是1的位置,复杂度O(NlogN).
#include <cstdio> #include <vector> #include <cstring> using namespace std; const int maxn = 50010; int node[maxn * 4], T, num[maxn], n; void pushup(int o) { node[o] = node[o * 2] + node[o * 2 + 1]; } void update(int l, int r, int o, int v, int delta) { if (l == r) { node[o] += delta; return ; } else { int m = (l + r) / 2; if (v <= m) update(l, m, o * 2, v, delta); else update(m + 1, r, o * 2 + 1, v, delta); pushup(o); } } int find(int k, int o, int l, int r) { if (l == r) return l; else { int m = (l + r) / 2; if (node[o * 2 + 1] >= k) return find(k, o * 2 + 1, m + 1, r); else return find(k - node[o * 2 + 1], o * 2, l, m); } } int main() { scanf("%d", &T); while(T --) { memset(node, 0, sizeof(node)); vector <int> ans; scanf("%d", &n); num[0] = 0; for (int i = 1; i <= n; i++) { scanf("%d", &num[i]); update(1, n, 1, i, 1); } for (int i = n; i > 0; i--) { int k = num[i] - num[i - 1] + 1; int x = find(k, 1, 1, n); ans.push_back(x); update(1, n, 1, x, -1); } for (int i = n - 1; i > 0; i--) printf("%d ", ans[i]); printf("%d\n", ans[0]); } return 0; }