HDU 5592:ZYB's Premutation 树状数组+二分

来源:互联网 发布:linux内核入门书籍 编辑:程序博客网 时间:2024/06/15 15:39

ZYB's Premutation

 
 Accepts: 218
 
 Submissions: 983
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYBZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列.(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当A_i>A_jAi>Aj
输入描述
第一行一个整数TT表示数据组数。接下来每组数据:第一行一个正整数NN,描述排列的长度.第二行NN个正整数A_iAi,描述前缀区间[1,i][1,i]的逆序对数.数据保证合法.1 \leq T \leq 51T5,1 \leq N \leq 500001N50000
输出描述
TT行每行NN个整数表示答案的排列.
输入样例
130 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]);elseprintf(" %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;}


0 0