ZYB's Premutation
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 51≤T≤5,1 \leq N \leq 500001≤N≤50000
输出描述
TT行每行NN个整数表示答案的排列.
输入样例
1 3 0 1 2
输出样例
3 1 2
题解:设f_ifi是第ii个前缀的逆序对数,p_ipi是第ii个位置上的数,则f_i-f_{i-1}fi−fi−1是ii前面比p_ipi大的数的个数.我们考虑倒着做,当我们处理完ii后面的数,第ii个数就是剩下的数中第f_i-f_{i-1}+1fi−fi−1+1大的数,用线段树和树状数组可以轻松地求出当前第kk个是11的位置,复杂度O(N \log N)O(NlogN).
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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; }
二分法
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5+20, M = 30005, mod = 1e9+7, inf = 0x3f3f3f3f; typedef long long ll; //不同为1,相同为0 int n; struct BIT { int tree[N] ; int lowbit(int x) { return x&(-x); } void add(int x, int add, int n) { for (; x <= n; x += lowbit(x)) { tree[x] += add; } } int sum(int x) { int s = 0; for (; x > 0; x -= lowbit(x)) { s += tree[x]; } return s; } void clears() { memset(tree, 0, sizeof tree); } }Bit; int cal(int x) { int l = 1, r = n, ret = -1; while(l<=r) { int mid = (l+r)>>1; int G = Bit.sum(n) - Bit.sum(mid-1); if(G>=x) l = mid+1, ret = mid; else r = mid-1; } return ret; } int main() { int T,b[N],a[N],ans[N]; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i] = a[i] - a[i-1]; Bit.clears(); for(int i=1;i<=n;i++) Bit.add(i,1,n); int pos = n - b[n]; ans[n] = pos; Bit.add(pos,-1,n); for(int i = n-1;i >= 1;i--) { pos = cal(b[i]+1); ans[i] = pos; Bit.add(pos,-1,n); } for(int i=1;i<n;i++) { printf("%d ",ans[i]); } printf("%d\n",ans[n]); } return 0; }