康托展开

康托展开

康托展开一般用于求一个序列的字典序,或是根据字典序求序列。
我们看这样一个序列 :

1 2 3 4 5

如果序列变成:

1 2 4 3 5

其字典序该怎么算呢?

\(3*(5-4)!=3\)

根据这个我们可以推出一个简单的式子:一个序列的字典序为:

\(\sum_{i=1}^{n}(rank(a_i)-1)*(n-i)!\)

其中,\(rank(a_i)\) 为出现在 \([a_{i+1},a_n]\) 之前是第 \(rank(a_i)\) 大的。
就比如说上面序列里的 \(4\),其 \(rank(a_i) = 3\)

这里康托展开就结束了,朴素算法为 \(O(n^2)\)

接下来一道例题:

luoguP5367康托展开

这道题很明显我们并不能用 \(O(n^2)\) 的算法去做,因此我们要考虑优化:

\(O(n^2)\) 的算法主要是多了一层计算 \(rank(a_i)\) ,那么我们怎么快速去算这个呢?

这时候就要用到树状数组了:先对每个数进行 \(add(i,1)\) 然后删除 \(a_i\) 只需要 \(add(a[i],-1)\)即可,\(sum\) 就是对应的 \(rank(a_i)\)

接下来是代码:

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&-x
const int mod=998244353;
long long a[1000005],n;
long long inv[1000005],c[1000005];
long long ans=0;
void add(int x,int k){
    for(;x<=n;x+=lowbit(x)) c[x]+=k; 
}
long long sum(int x){
    int res=0;
    for(;x;x-=lowbit(x)) res+=c[x];
    return res;
}
int main()
{
    cin>>n;inv[1]=inv[0]=1;
    for(int i=2;i<=n;i++)   inv[i]=i*inv[i-1]%mod;
    for(int i=1;i<=n;i++)   add(i,1);
    for(int i=1;i<=n;i++){
        scanf(" %d",&a[i]);
        ans=(ans+((sum(a[i])-1)*inv[n-i])%mod)%mod;
        add(a[i],-1);
    }
    cout<<ans+1<<endl;
    system("pause");
    return 0;
}

逆向康托展开:

我们既然已经知道一个序列的字典序数,那么有什么办法可以求这个序列的每一位是多少呢?

这里就是康托逆展开了。

我们可以假设一个序列:

1 2 3 4 5,要求的序列其字典序数为46。

过程:

  1. \(46-1=45\) 代表有45个序列比这个序列小,
  2. \(\lfloor\dfrac{45}{4!}\rfloor=1\) ,说明有一个数小于它,因此第一位是2。
  3. 此时让排名减去 \(1*4!\) 得到 21,\(\lfloor\dfrac{21}{3!}\rfloor=3\),说明有3个数小于它,这一位就是5。
  4. \(21-3*3!=3,\lfloor\dfrac{3}{2!}\rfloor=1\),说明有1个数小于它,这一位就是3。
  5. \(3-1*2!=1\),有1个数小于它,这一位就是剩下来的第二位,4,最后一位就是1。

那么我们就可以得到序列:

2 5 3 4 1

实际上我们得到了形如 有两个数小于它 这一结论,就知道它是当前第3个没有被选上的数,这里也可以用线段树维护,时间复杂度为 \(O(n\log n)\)

暴力代码:

vector<int> v,a;
inv[0]=1;
for(int i=1;i<=n;i++) inv[i]=inv[i-1]*i;
for(int i=1;i<=n;i++) v.push_back(i);
for(int i=n;i>=1;i--){
    int r=x%inv[i-1];
    int t=x/inv[i-1];
    x=r;
    sort(v.begin(),v.end());
    a.push_back(v[t]);
    v.erase(v.begin()+t);
}
for(int i=0;i<a.size();i++) cout<<a[i]<<" ";
posted @ 2021-04-06 16:32  Evitagen  阅读(89)  评论(0)    收藏  举报