康托展开与逆康托展开

前言

这个东西也算比较常见吧,所以来记录下

作用

康托展开的实质是计算当前排列在所有由小到大全排列中的顺序

逆康托展开即已知在所有由小到大全排列中的顺序求排列

介绍

首先康托展开的公式

\(x=a_i(n-1)!+a_{i-1}(n-2)!...a_{1}(0)!\)

\(a_i\)表示原数的第\(i\)位在当前未出现的元素中有几个比他小

感觉这个证明挺简单的,不做说明

那么如何计算\(a_i\)呢,首先可以暴力\(O(n^2)\)求出所有\(a_i\)

显然要优化

设当前值为\(s\),且其对应的\(a_i\)\(t\),说明未出现的元素有\(t\)个元素比\(s\)

那么说明在已经出现的元素中有\(s-1-t\)个元素比\(s\)

利用线段求前面有多少个元素比自己小即可

实例

题目链接

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,mod=998244353,inf=0x3f3f3f3f;
int n,a[maxn];
int tree[maxn<<2];
int fac[maxn];
int query(int node,int L,int R,int l,int r){
    if(L>R) return 0;
    if(L<=l&&r<=R){
        return tree[node];
    }
    int mid=(l+r)>>1,sum=0;
    if(mid>=L) sum+=query(node<<1,L,R,l,mid);
    if(mid<R) sum+=query(node<<1|1,L,R,mid+1,r);
    return sum;
}
void update(int node,int l,int r,int pos){
    if(l==r){
        tree[node]=1;
        return ;
    }
    int mid=(l+r)>>1;
    if(mid>=pos) update(node<<1,l,mid,pos);
    else update(node<<1|1,mid+1,r,pos);
    tree[node]=tree[node<<1]+tree[node<<1|1];
}
int main(){
    scanf("%d",&n);
    fac[0]=1;
    for(int i=1;i<=n;i++){
        fac[i]=1ll*fac[i-1]*i%mod;
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    ll ans=1;
    for(int i=1;i<=n;i++){
        ans=(ans+1ll*(a[i]-query(1,1,a[i]-1,1,n)-1)*fac[n-i])%mod;
        update(1,1,n,a[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

逆康托展开其实就是一个相反的操作

实例

题目链接

题目思路

就是逆操作,权值线段树上二分即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,mod=998244353,inf=0x3f3f3f3f;
int n,a[maxn];
int tree[maxn<<2];
int query(int node,int l,int r,int val){
    tree[node]--;
    if(l==r){
        return l;
    }
    int mid=(l+r)>>1,ans;
    if(tree[node<<1]>=val)  ans=query(node<<1,l,mid,val);
    else ans=query(node<<1|1,mid+1,r,val-tree[node<<1]);
    return ans;
}
void build(int node,int l,int r){
    if(l==r){
        tree[node]=1;
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    tree[node]=tree[node<<1]+tree[node<<1|1];
}
int main(){
    int _; scanf("%d",&_);
    while(_--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        build(1,1,n);
        for(int i=1;i<=n;i++){
            printf("%d%c",query(1,1,n,a[i]+1),i==n?'\n':' ');
        }
    }
    return 0;
}

posted @ 2021-04-20 19:22  hunxuewangzi  阅读(80)  评论(0编辑  收藏  举报