啊。。。好久没写了。。。可能是最后一篇学习笔记了吧
题目大意:给定序列求其在全排列中的排名&&给定排名求排列。
这就是康托展开&&逆康托展开要干的事了。下面依次介绍
一、康托展开
首先,知道它是干嘛的。
就是给定一个全排列之中的序列,求其在整个全排列中的排名。
给出式子:
$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=;
for(ll i=;i<=n;i++)
{
ll cnt=;
for(ll j=i+;j<=n;j++)
{
if(a[i]>a[j])//对每一位考虑
cnt++;
}
ans+=cnt*fac[n-i];
}
return ans+;//因为求的是前有多少,所有排名+1
}
二、逆康托展开
好了,那有了排名怎么求数组呢?
由上述康托展开可得,要得到数组的每一位,就必须确定前面有多少比它大的。
于是反过来,对每一位考虑可以由多少比它大的,也就是求上述式子中括号里的东西,然后一位一位还原,就成了原序列
过程:首先,同上,-1
然后对每一位,把序号除以对应的fac,确定一个没用过的数,作为当前的答案即可
代码片:
ll nkt(ll k)
{
k-=;
ll j;
memset(vis,,sizeof(vis));
for(ll i=;i<=n;i++)
{
ll s=k/fac[n-i];
for(j=;j<=n;j++)
{
if(!vis[j])
{
if(!s)
break;
s--;
}
}
printf("%d ",j);
vis[j]=;
k%=fac[n-i];
}
printf("\n");
}
(完)