康托展开(Cantor Expansion)学习笔记
定义
公式
\(X=a_{1}*(n-1)!+a_{2}*(n-2)!+\)...\(+a_{n}*0!\)
即\(X=\sum_{i=1}^{n}a_{i}*(n-i)!\)
\(X\)为比当前排列小的排列个数,\(a_{i}\)是比第\(i\)个(从左到右数)小的且在第\(i\)个右边的数的个数
逆康托展开
由于康托展开是那种“反之亦然”的东西,所以当你知道\(X\),你能求出第\(X+1\)个排列
例题
#include<bits/stdc++.h>
using namespace std;
int n,m,fac[11],vis[11];
vector<int>v;
void Cantor(int x)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
int now=x/fac[n-i];
for(int j=1;j<=n;j++)
{
if(!vis[j])
{
if(!now)
{
vis[j]=1;
printf("%d ",j);
break;
}
now--;
}
}
x%=fac[n-i];
}
printf("\n");
}
int main()
{
cin>>n;
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i;
}
for(int i=0;i<fac[n];i++)
Cantor(i);
}
方法就是。。。算了照上面的式子反着推吧,不想写
应用
用于求解第几个排列,某排列是第几个的问题,以及哈希
应用例题分析
例题
思路:板子,用树状数组优化
#include<bits/stdc++.h>
using namespace std;
int n,s[1000010],tree[1000010],l[1000010];
int mod=998244353;
long long fac[1000010],ans;
void add(int x,int k)
{
for(;x<=n;x+=l[x])
{
tree[x]+=k;
}
}
int get_sum(int x)
{
int res=0;
for(;x>0;x-=l[x])
{
res+=tree[x];
}
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i]);
}
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%mod;
l[i]=i&-i;
}
for(int i=1;i<=n;i++)
{
add(i,1);
}
for(int i=1;i<=n;i++)
{
ans=(ans+((get_sum(s[i])-1)*fac[n-i])%mod)%mod;
add(s[i],-1);
}
cout<<ans+1;
}