标签:
ZYBZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列.
(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当A_i>A_jA?i??>A?j??
第一行一个整数TT表示数据组数。
接下来每组数据:
第一行一个正整数NN,描述排列的长度.
第二行NN个正整数A_iA?i??,描述前缀区间[1,i][1,i]的逆序对数.
数据保证合法.
1 \leq T \leq 51≤T≤5,1 \leq N \leq 500001≤N≤50000
TT行每行NN个整数表示答案的排列.
1 3 0 1 2
3 1 2
题解:设f_if?i??是第ii个前缀的逆序对数,p_ip?i??是第ii个位置上的数,则f_i-f_{i-1}f?i??−f?i−1??是ii前面比p_ip?i??大的数的个数.我们考虑倒着做,当我们处理完ii后面的数,第ii个数就是剩下的数中第f_i-f_{i-1}+1f?i??−f?i−1??+1大的数,用线段树和树状数组可以轻松地求出当前第kk个是11的位置,复杂度O(N \log N)O(NlogN).
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 5e4 + 500; long long arr[maxn],b[maxn]; #define lowbit(x) ((x)&(-x)) struct BinaryIndexTree { int val[maxn],sz; void init(int sz){ this->sz=sz; memset(val , 0 , sizeof(int)*(sz+5)); } void updata(int pos ,int key){ while(pos<=sz){ val[pos]+=key; pos+=lowbit(pos); } } int prefixsum(int pos){ int res=0; while(pos>0){ res+=val[pos]; pos-=lowbit(pos); } return res; } int query(int l,int r){ return prefixsum(r)-prefixsum(l-1); } //找到第一个大于等于k的位置返回 //若不存在,返回-1 int lower_bound(int k){ if(prefixsum(sz)<k) return -1; int l = 1 , r = sz; while(l <= r){ int mid = l + ((r-l)>>1); if(prefixsum(mid) < k) l = mid + 1; else r = mid - 1; } return l; } }solver; int ans[maxn]; int main(int argc,char *argv[]){ int Case; scanf("%d",&Case); while(Case--){ int n; scanf("%d",&n); solver.init(n); for(int i = 1 ; i <= n ; ++ i) scanf("%I64d",arr+i); for(int i = 1 ; i <= n ; ++ i) b[i] = arr[i] - arr[i-1]; for(int i = 1 ; i <= n ; ++ i) solver.updata(i , 1); for(int i = n ; i >= 1 ; -- i){ long long rank = i - b[i]; int t = solver.lower_bound(rank); ans[i] = t; solver.updata(t,-1); } printf("%d",ans[1]); for(int i = 2 ; i <= n ; ++ i) printf(" %d",ans[i]); printf("\n"); } return 0; }
hdu5592/BestCoder Round #65 树状数组寻找第K大
标签:
原文地址:http://www.cnblogs.com/zxhl/p/5022465.html