Let's define the sum of two permutations p and q of numbers 0, 1, ..., (n - 1) as permutation , where Perm(x) is the x-th lexicographically permutation of numbers 0, 1, ..., (n - 1) (counting from zero), and Ord(p) is the number of permutation p in the lexicographical order.
For example, Perm(0) = (0, 1, ..., n - 2, n - 1), Perm(n! - 1) = (n - 1, n - 2, ..., 1, 0)
Misha has two permutations, p and q. Your task is to find their sum.
Permutation a = (a0, a1, ..., an - 1) is called to be lexicographically smaller than permutation b = (b0, b1, ..., bn - 1), if for some k following conditions hold: a0 = b0, a1 = b1, ..., ak - 1 = bk - 1, ak < bk.
The first line contains an integer n (1 ≤ n ≤ 200 000).
The second line contains n distinct integers from 0 to n - 1, separated by a space, forming permutation p.
The third line contains n distinct integers from 0 to n - 1, separated by spaces, forming permutation q.
Print n distinct integers from 0 to n - 1, forming the sum of the given permutations. Separate the numbers by spaces.
2 0 1 0 1
0 1
2 0 1 1 0
1 0
3 1 2 0 2 1 0
1 0 2
1-N,给你两个排列,把这两个排列在全排列中的序号加起来,对N!取模得到一个数k,输出N个数的第k个排列。
做这个题又让我回顾了一下以前做过的一个线段树加速康托展开,因为每次都要判断还未出现过的比当前数小的数有几个,如果一个一个数插入,用线段树或者树状数组判断当前已经出现了几个比这个数小的,复杂度是O(lgn)。
首先要把两个排列化成(p1[n-1]+p2[n-1])*(n-1)!+(p1[n]+p2[n])*n!+...(p1[0]+p2[0])*0!的形式,因为系数p[n]不能大于n,单独算p1,p2不会超,两个加起来可能就会超了,不符合这种形式,所以要进位。p1[0]和p2[0]肯定都是0,从1开始,如果p1[i]+p2[i]大于i,那么就把i+1的系数加一,i的系数减去i+1,到最高位也就是(p1[n-1]+p2[n-1])*(n-1)!,这里不用再进位,因为要对N!取模,也就相当于(p1[n-1]+p2[n-1])对n取模。
到这里得到了一个符合条件的康托展开式,最后就是把这个式子化成对应的排列。思路是利用树状数组+二分,先初始化一个每位都是1的树状数组,每次对p[i]进行二分查找位置,找到对应的位置,这个位置之前有p[i]个1,并且这个位置也是1。再把这个位置的1减掉。复杂度O(n*lgn*lgn)。
#include<iostream> #include<queue> #include<cstring> #include<cstdio> #include<cmath> #include<set> #include<map> #include<vector> #include<stack> #include<algorithm> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int MAXN=200010; int N,a[MAXN],b[MAXN],c1[MAXN],c2[MAXN],c3[MAXN],p[MAXN],ans[MAXN]; int lowbit(int x){ return x&(-x); } void add(int* c,int i,int v){ while(i<MAXN){ c[i]+=v; i+=lowbit(i); } } int getsum(int* c,int i){ int ret=0; while(i){ ret+=c[i]; i-=lowbit(i); } return ret; } int bsearch(int pos,int l,int r){ //(l,r] while(l<r){ int mid=l+(r-l)/2,n=getsum(c3,mid); if(n<pos) l=mid+1; else r=mid; } return r; } int main(){ freopen("in.txt","r",stdin); while(scanf("%d",&N)!=EOF){ for(int i=0;i<N;i++) scanf("%d",&a[i]); for(int i=0;i<N;i++) scanf("%d",&b[i]); memset(c1,0,sizeof(c1)); memset(c2,0,sizeof(c2)); memset(c3,0,sizeof(c3)); for(int i=1;i<=N;i++) add(c3,i,1); for(int i=0;i<N;i++){ int n=getsum(c1,a[i]+1); p[N-i-1]=a[i]-n; add(c1,a[i]+1,1); } for(int i=0;i<N;i++){ int n=getsum(c2,b[i]+1); p[N-i-1]+=b[i]-n; add(c2,b[i]+1,1); } for(int i=1;i<N-1;i++) if(p[i]>i){ p[i+1]++; p[i]-=i+1; } p[N-1]%=N; for(int i=N-1;i>=0;i--){ int n=bsearch(p[i]+1,0,N); ans[N-i-1]=n-1; add(c3,n,-1); } for(int i=0;i<N-1;i++) printf("%d ",ans[i]); printf("%d\n",ans[N-1]); } return 0; }