康托展开&逆康托展开学习笔记

啊。。。好久没写了。。。可能是最后一篇学习笔记了吧

题目大意:给定序列求其在全排列中的排名&&给定排名求排列。

这就是康托展开&&逆康托展开要干的事了。下面依次介绍

一、康托展开

首先,知道它是干嘛的。

就是给定一个全排列之中的序列,求其在整个全排列中的排名。

给出式子:
$k=sum_{i=1}^n(n-i)!\sum_{j=i+1}^n(a_{k,i}>a_{k,j})$

解释一下:考虑这个序列的第i位,对于这个序列,只有前i位都小于等于它,第i位一定小于它的所有序列才会在它前面,于是对每一位考虑组合,就是这个结果了。

代码片:

ll ktz(ll *a)
{
    ll ans=0;
    for(ll i=1;i<=n;i++)
    {
        ll cnt=0;
        for(ll j=i+1;j<=n;j++)
        {
            if(a[i]>a[j])//对每一位考虑
            cnt++;
        }
        ans+=cnt*fac[n-i];
    }
    return ans+1;//因为求的是前有多少,所有排名+1
}

二、逆康托展开

好了,那有了排名怎么求数组呢?

由上述康托展开可得,要得到数组的每一位,就必须确定前面有多少比它大的。

于是反过来,对每一位考虑可以由多少比它大的,也就是求上述式子中括号里的东西,然后一位一位还原,就成了原序列

过程:首先,同上,-1

然后对每一位,把序号除以对应的fac,确定一个没用过的数,作为当前的答案即可

代码片:

ll nkt(ll k)
{
    k-=1;
    ll j; 
    memset(vis,0,sizeof(vis));
    for(ll i=1;i<=n;i++)
    {
        ll s=k/fac[n-i];
        for(j=1;j<=n;j++)
        {
            if(!vis[j])
            {
                if(!s)
                break;
                s--;
            }
        }
        printf("%d ",j);
        vis[j]=1;
        k%=fac[n-i];
    }
    printf("\n");
}

(完)

posted @ 2019-10-19 00:45  阿基米德的澡盆  阅读(121)  评论(0编辑  收藏  举报