ZYB's Premutation
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 635 Accepted Submission(s): 300
Problem Description
ZYB
has a premutation
P
,but he only remeber the reverse log of each prefix of the premutation,now he ask you to
restore the premutation.
Pair (i,j)(i<j) is considered as a reverse log if Ai>Aj is matched.
restore the premutation.
Pair (i,j)(i<j) is considered as a reverse log if Ai>Aj is matched.
Input
In the first line there is the number of testcases T.
For each teatcase:
In the first line there is one number N .
In the next line there are N numbers Ai ,describe the number of the reverse logs of each prefix,
The input is correct.
1≤T≤5 , 1≤N≤50000
For each teatcase:
In the first line there is one number N .
In the next line there are N numbers Ai ,describe the number of the reverse logs of each prefix,
The input is correct.
1≤T≤5 , 1≤N≤50000
Output
For each testcase,print the ans.
Sample Input
1 3 0 1 2
Sample Output
3 1 2
首先,非常感谢磊神指点,基本上copy磊神的代码
题目的意思很清楚,相当于给个密码,然后解码,发现一个规律,比如例子给的3 1 2,每一位的逆序对数是0 1 2,倒过来将每一位与前一位做差得0 1 1,为什么要倒过来呢,因为你新加入一个数,比如说在3 1的基础上加入2,2的加入对于前面的逆序对数是没有影响的,所以后减前得到的值可以理解为是由于这个数的加入,在原先的基础上增加了多少个逆序对,也就是可以反向知道一个数在原来的所有数中排第几
比如0 1 2,倒序相减以后得到0 1 1,从后往前看,第二个1的意思是第三位是1 2 3中第1 + 1 = 2大的数就是2,现在确定了第三位自然要将其排除,然后看第二个1的意思是在
剩下的1 3中第1 + 1 = 2大的数即1,再排除一个,现在1 2 3这个集合中只剩下3了,然后最后一个0,意思是在剩下的当中第0 + 1 = 1大的数,刚好也只剩一个3,最后输出3
综上倒序输出的顺序是2 1 3,反过来就是原来的序列3 1 2
但是这个问题解决了以后还没完,数据规模是50000,直接线性扫描找第几大的数肯定会超时,然后要用一个线段树来将找第几大的数的复杂度降到O(logN)
线段树上的数字表示此区间有多少个数,取出来一个,区间内的数的个数要减一
#include <cstdio>
#include <iostream>
#include <cstring>
#define lson rt << 1, l, m
#define rson rt << 1 | 1, m + 1, r
using namespace std;
const int MAX = 50050;
int sum[MAX << 2];
int a[MAX];
int val[MAX];
int ans[MAX];
void PushUP(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void build(int rt, int l, int r) {
if (l == r) {
sum[rt] = 1; //将每个叶子初始化为1
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
int query(int x, int rt, int l, int r) {
if (l == r) {
sum[rt] = 0; //取出来这个数,将区间内的数减少1个
return l;
}
int m = (l + r) >> 1;
int ans;
if (sum[rt << 1 | 1] >= x) { //因为右面的大,所以先考察右面孩子节点
ans = query(x, rson);
}
else {
ans = query(x - sum[rt << 1 | 1], lson);
}
PushUP(rt);
return ans;
}
int main()
{
int T, N;
cin >> T;
while (T--) {
scanf("%d", &N);
build(1, 1, N);
a[0] = 0; //初始化边界
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
}
for (int i = N; i >= 1; i--) {
int tem;
tem = a[i] - a[i - 1]; //倒过来计算
val[i] = query(tem + 1, 1, 1, N); //注意tem + 1
}
for (int i = 1; i <= N; i++) {
printf("%s%d", i == 1 ? "" : " ", val[i]);
}
puts("");
}
return 0;
}