题意:给两个排列,分别算出是第几小的排列,这两个数字求和以后再模n!得到一个数,输出这个数对应的排列。
思路:很明显的康托/逆康托展开。难点在于如何快速求解康托/逆康托以及模n!上。
在康托展开中,遍历每一位是在所难免的,时间复杂度是O(n),在统计比a[i]小的数字个数的时候显然不能遍历了,可以用树状数组加速,时间复杂度是O(lgn)。这里并不得到的数字加起来,因为可能达到n!,太大了,而是按i!,这样每一位的保存。这一步的总复杂度是O(nlgn)。
对a、b两个排列都进行康托展开,得到一个数组v,这是它们每一位i!的和。
这时候需要进行模n!。
数组v的组成是这样的v[n]*n!+v[n-1]*(n-1)!+…+v[1]*1!+v[0]*0!
可以发现如果v[0]大于1,那么它就可以进位给v[1]了,如果v[1]大于2那么它就可以进位给v[2]了。即v[i+1]+=v[i]/(i+1),v[i]=v[i]%(i+1)。这样就完成了模n!。
最后一步是逆康托展开。
显然数组v的每个数v[i]就是它模i!的商。所以问题就在于如何快速找到有k个比它小的数字的数是几。这一步使用二分解决。
思路:很明显的康托/逆康托展开。难点在于如何快速求解康托/逆康托以及模n!上。
在康托展开中,遍历每一位是在所难免的,时间复杂度是O(n),在统计比a[i]小的数字个数的时候显然不能遍历了,可以用树状数组加速,时间复杂度是O(lgn)。这里并不得到的数字加起来,因为可能达到n!,太大了,而是按i!,这样每一位的保存。这一步的总复杂度是O(nlgn)。
对a、b两个排列都进行康托展开,得到一个数组v,这是它们每一位i!的和。
这时候需要进行模n!。
数组v的组成是这样的v[n]*n!+v[n-1]*(n-1)!+…+v[1]*1!+v[0]*0!
可以发现如果v[0]大于1,那么它就可以进位给v[1]了,如果v[1]大于2那么它就可以进位给v[2]了。即v[i+1]+=v[i]/(i+1),v[i]=v[i]%(i+1)。这样就完成了模n!。
最后一步是逆康托展开。
显然数组v的每个数v[i]就是它模i!的商。所以问题就在于如何快速找到有k个比它小的数字的数是几。这一步使用二分解决。
#include<iostream>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=200005;
int n;
struct BIT
{
int dat[maxn];
void clear()
{
memset(dat,0,sizeof(dat));
}
int lowbit(int x)
{
return -x&x;
}
int getSum(int x)
{
int sum=0;
while(x>0)
{
sum+=dat[x];
x-=lowbit(x);
}
return sum;
}
void add(int x,int val)
{
while(x<=n)
{
dat[x]+=val;
x+=lowbit(x);
}
}
};
int a[maxn],b[maxn];
int v[maxn];
BIT bit;
void kuangtuo(int *arr,BIT &bit)
{
bit.clear();
for(int i=1; i<=n; ++i)
{
int s=arr[i]-1-bit.getSum(arr[i]-1);
bit.add(arr[i],1);
v[i]+=s;
}
}
int c[maxn];
int Bsearch(int low,int high,int K)
{
int mid=low+(high-low)/2;
while(low<high)
{
if((mid-bit.getSum(mid))>=K) high=mid;
else low=mid+1;
mid=low+(high-low)/2;
}
return mid;
}
void nikuangtuo(int *arr,BIT &bit)
{
bit.clear();
for(int i=1; i<=n; ++i)
{
int p=Bsearch(1,n,(v[i]+1));
arr[i]=p;
bit.add(p,1);
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; ++i)
{
int x;
scanf("%d",&x);
a[i]=x+1;
}
for(int i=1; i<=n; ++i)
{
int x;
scanf("%d",&x);
b[i]=x+1;
}
kuangtuo(a,bit);
kuangtuo(b,bit);
for(int i=n; i>=1; --i)
{
int j=n-i+1;
v[i-1]+=v[i]/j;
v[i]=v[i]%j;
}
nikuangtuo(c,bit);
for(int i=1; i<=n; ++i)
{
if(i!=1) printf(" ");
printf("%d",c[i]-1);
}
printf("\n");
return 0;
}