康托展开(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; 
}

题目

火星人
康托展开
牛线

posted @ 2019-07-09 12:27  G_A_TS  阅读(587)  评论(1编辑  收藏  举报