• 康托展开与逆康托展开


    前言

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

    作用

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

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

    介绍

    首先康托展开的公式

    (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
    ",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?'
    ':' ');
            }
        }
        return 0;
    }
    
    
    卷也卷不过,躺又躺不平
  • 相关阅读:
    .NET基础知识之七——索引器
    安装Sql Server 2008的时候报错说找不到某个安装文件
    安装Sql Server 2008时出错
    英文操作系统奇怪的问题
    装虚拟机后一部分磁盘空间消失
    服务器允许两台电脑远程登录一个账户
    Linq工具篇(1)——使用LinqPad
    ExtJs工具篇(1)——在Aptana3中安装ExtJS 代码提示插件
    SQL基础(三)-- SQL根据”,“分割字符串
    SQL基础(四)-- SQL连接时去掉重复数据
  • 原文地址:https://www.cnblogs.com/hunxuewangzi/p/14682481.html
Copyright © 2020-2023  润新知