为什么我在EN-WIKI上查不到啊TAT
嗯,康托展开就是一个从n排列集合到自然数集合的映射.
并且这个映射刚好对应了字典序.
比如,设这个函数为C,
那么C{1,2,3}=0,C{3,2,1}=5,C{2,3,1}=3.
用于排列与数字的转换.
使用的时候一定要注意先把排列离散化成0,1,...,n-1的排列再做.
否则要用n^2的时间来找"在全集中比数i小的数个数." 离散化以后直接就是a[i]+1就好做一些.
离散化之后可以用树状数组或者线段树或者平衡树做到nlogn.
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdlib>
#include <algorithm>
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
int getint()
{
int res=0; char c=getchar(); bool m=false;
while(c<'0' || c>'9') m=(c=='-'),c=getchar();
while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
return m ? -res : res;
}
const db eps=1e-18;
bool feq(db a,db b)
{ return fabs(b-a)<eps; }
using namespace std;
int a[50]={0,1,2,3,4,5};
int getpow(int i)
{
int res=1;
while(i>0) res*=i,i--;
return res;
}
int Cantor(int*a,int n)
{
int res=0;
for(int i=0;i<n;i++)
{
int cnt=0;
for(int j=0;j<i;j++) if(a[j]<a[i]) cnt++;
res+=getpow(n-i-1)*max(0,a[i]-cnt);
}
return res;
}
bool used[50];
int*RevCantor(int X,int n)
{
int*res=new int[n+1];
memset(used,0,sizeof(bool)*(n+1));
for(int i=0;i<n;i++)
{
int p=X/getpow(n-i-1);
X=X-p*getpow(n-i-1);
int j=0;
for(;used[j]||p>0;j++)
if(!used[j]) p--;
res[i]=j;
used[j]=true;
}
return res;
}
int main()
{
for(int i=0;i<10;i++)
{
int c=Cantor(a,6);
cout<<i<<' '<<c<<endl;
for(int i=0;i<6;i++) cout<<a[i]<<' '; cout<<endl;
next_permutation(a,a+6);
int*p=RevCantor(c,6);
for(int i=0;i<6;i++) cout<<p[i]<<' '; cout<<endl;
delete(p);
}
return 0;
}