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;
}