ZYB's Premutation
Accepts: 218
Submissions: 983
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
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
可以通过这个序列得到前面比自己小的数,进而知道自己正常情况下排到的位置。但是如果后面有数已经占到了这个位置,就要相应地往后面排。用树状数组+二分 找到有空的那个位置。从后往前扫。
代码:
#pragma warning(disable:4996)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
int n;
int val[50005];
int ans[50005];
int wc[50005];
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int y)
{
while (x <= n)
{
ans[x] = ans[x] + y;
x = x + lowbit(x);
}
}
int sum(int x)
{
int res = 0;
while (x > 0)
{
res += ans[x];
x = x - lowbit(x);
}
return res;
}
int cal(int x)
{
int ri = n;
int le = 1;
int mid;
while (le <= ri)
{
mid = (le + ri) >> 1;
if (sum(mid) >= x)
{
ri = mid - 1;
}
else
{
le = mid + 1;
}
}
return le;
}
void solve()
{
int i;
for (i = 1; i <= n; i++)
{
add(i, 1);
}
for (i = n; i >= 1; i--)
{
int pos = i - val[i] - 1;//前面有多少比它小的数
wc[i] = cal(pos + 1);
add(wc[i], -1);
}
for (i = 1; i <= n; i++)
{
if (i == 1)
printf("%d", wc[i]);
else
printf(" %d", wc[i]);
}
printf("\n");
}
int main()
{
//freopen("i.txt", "r", stdin);
//freopen("o.txt", "w", stdout);
int t;
int i;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
memset(ans, 0, sizeof(ans));
for (i = 1; i <= n; i++)
{
scanf("%d", val + i);
}
for (i = n; i >= 1; i--)
{
val[i] = val[i] - val[i - 1];
}
solve();
}
//system("pause");
return 0;
}