zoukankan      html  css  js  c++  java
  • 康托展开与逆康托展开

    前言

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

    作用

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

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

    介绍

    首先康托展开的公式

    (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;
    }
    
    
    卷也卷不过,躺又躺不平
  • 相关阅读:
    随机森林算法参数调优
    BAYES和朴素BAYES
    阿里云 金融接口 token PHP
    PHP mysql 按时间分组 表格table 跨度 rowspan
    MySql按周,按月,按日分组统计数据
    PHP 获取今日、昨日、本周、上周、本月的等等常用的起始时间戳和结束时间戳的时间处理类
    thinkphp5 tp5 会话控制 session 登录 退出 检查检验登录 判断是否应该跳转到上次url
    微信 模板消息
    php 腾讯 地图 api 计算 坐标 两点 距离 微信 网页 WebService API
    php添加http头禁止浏览器缓存
  • 原文地址:https://www.cnblogs.com/hunxuewangzi/p/14682481.html
Copyright © 2011-2022 走看看