zoukankan      html  css  js  c++  java
  • [学习笔记]康托展开

    康托展开

    一.什么是康托展开

    ​ 康托展开适用于计算一个排列结果在字典序全排列中的序号(很显然序号与这个排列形成一种双射关系),利用此算法可以构造排列的Hash表。

    二.实现康托展开

    ​ 举个例,在集合({1,2,3})中选取三个数的全排列,问 3,2,1 是第几个。

    对于这个问题,如果说让我用手来算的话,我会使用分类讨论比他小的排列,见下图。

    至于累积的结果是否需要+1,取决于编号从0还是1开始。大致总结一下,我们可以得出康托展开的一般形式。

    [ans=sum^{n}_{i=1}p_i*(n-i)! ]

    其中(P_i)为在未选取的元素中比排列结果中的第i个数小的元素个数。在下文称之为系数。

    三.求解系数

    ​ 很简单的,我们能过想到接近于(O(n^2))复杂度的暴力与vector求解,但是我们需要一个更高级的算法。

    ​ 如果说我们能用0,1表示某个元素是否存在,(我们使得初始集合中的元素成升序排列),那么对于前缀和而言,所代表的涵义就是比他小的元素个数,于是用此方法就可以快速求解系数。当这个数选取了之后,我们需要把它标记为不存在,前缀和涉及到修改,我们可以用树状数组求解。

    四.Code(Luogu板子)

    ll mod=998244353;
    ll n,c[1000005];
    void add(int x,int y){
    	for(;x<=n;x+=x&(-x))c[x]+=y;
    }
    int sum(int x){
    ll res=0;
    while(x>0){
    	res+=c[x];
    	res%=mod;
    	x-=x&(-x);
    }	
    return res-1;
    }
    ll now[1000005],ans,jc[1000005]={1};
    void ready(){
    	for(int i=1;i<=n;++i)jc[i]=(jc[i-1]*i)%mod;
    }
    int main(){
    	cin>>n;
    	ready();
    	for(int i=1;i<=n;++i){
    		cin>>now[i];
    		add(i,1);
    	}
    	for(int i=1;i<=n;++i){
    		ans=(ans+jc[n-i]*sum(now[i]))%mod;
    		add(now[i],-1);
    	}
    	cout<<(ans+1)%mod;
    	return 0;
    }
    

    五.逆康托展开

    ​ 在知道了康托展开的原理之后,我们其实可以通过序号求解排列(前文有提到他们是双射关系),于是考虑求解。

    ​ 我们可以知道一个东西(易证得)

    [P_k(n-k)!>sum^{k-1}_{i=1}P_i(n-i)! ]

    于是我们对每一个((n-i)!)进行取模与除就可以反向推出每一项的系数。得到系数以后,由于系数即代表大小关系,所以令(i=P_i+1),之后我们需要通过一个算法来求出第i小的元素,输出并删除,这里我用的是平衡树(FHQ)

    挂一个已知系数求解元素的代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=50010;
    int size_node[N],children[N][2],number_tree[N],number_heap[N],total,root, number_data_sets,order,x;
    void update(int node_now) {size_node[node_now]=size_node[children[node_now][0]]+size_node[children[node_now][1]]+1;}
    void split(int node_now,int k,int &tree_accept,int &tree_unaccept) {
        if(!node_now){
            tree_accept=tree_unaccept=0;
            return;
        }
        if(number_tree[node_now]>k) {
            tree_unaccept=node_now;
            split(children[node_now][0],k,tree_accept,children[node_now][0]);
        } else {
            tree_accept=node_now;
            split(children[node_now][1],k,children[node_now][1],tree_unaccept);
        }
        update(node_now);
    }
    int node_add(int k) {number_heap[++total]=rand(),number_tree[total]=k,size_node[total]=1;return total;}
    int merge(int tree_one,int tree_two) {
        if(!tree_one||!tree_two)return tree_one+tree_two;
        if(number_heap[tree_one]<number_heap[tree_two]) {
            children[tree_one][1]=merge(children[tree_one][1],tree_two);
            update(tree_one);
            return tree_one;
        } else {
            children[tree_two][0]=merge(tree_one,children[tree_two][0]);
            update(tree_two);
            return tree_two;
        }
    }
    void Insert(int k) {
        int tree_one,tree_two;
        split(root,k,tree_one,tree_two);
    	root=merge(merge(tree_one,node_add(k)),tree_two);
    }
    void extrack(int k) {
        int tree_less,tree_unless,tree_k;
        split(root,k,tree_less,tree_unless);
        split(tree_less,k-1,tree_less,tree_k);
     	tree_k=merge(children[tree_k][0],children[tree_k][1]);
        root=merge(tree_less,merge(tree_k,tree_unless));
    }
    int frank(int node_now,int k) {
        while(1) {
    		if(size_node[children[node_now][0]]>=k)node_now=children[node_now][0];
            else if(size_node[children[node_now][0]]+1<k)k-=size_node[children[node_now][0]]+1,node_now=children[node_now][1];
            else return number_tree[node_now];
        }
    }
    ll k,n,s[N];
    int main(){
    	cin>>k;
    	while(k--){
    		total=0;
    		scanf("%d",&n);
    		for(int i=1;i<=n;++i){
    			Insert(i);
    			cin>>s[i];
    		}
    		for(int i=1;i<=n;++i){
    			int u=frank(root,s[i]+1);
    			printf("%d ",u);
    			extrack(u);
    		}
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    F版本SpringCloud1—大白话为啥要有微服务?啥是微服务?SpringCloud为什么有那么多组件?
    Java已五年1—二本物理到前端实习生到Java程序员「回忆贴」
    SpringBoot图文教程17—上手就会 RestTemplate 使用指南「Get Post」「设置请求头」
    SpringBoot图文教程15—项目异常怎么办?「跳转404错误页面」「全局异常捕获」
    SpringBoot图文教程14—SpringBoot集成EasyExcel「上」
    SpringBoot图文教程12—SpringData Jpa的基本使用
    SpringBoot图文教程11—从此不写mapper文件「SpringBoot集成MybatisPlus」
    SpringBoot图文教程10—模板导出|百万数据Excel导出|图片导出「easypoi」
    SpringBoot图文教程9—SpringBoot 导入导出 Excel 「Apache Poi」
    SpringBoot图文教程8 — SpringBoot集成MBG「代码生成器」
  • 原文地址:https://www.cnblogs.com/clockwhite/p/12217947.html
Copyright © 2011-2022 走看看