zoukankan      html  css  js  c++  java
  • 【算法进阶-康托展开】-C++

    引入


    这位老爷子就是康托

    基本概念

    康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

    所以,康托展开是为了把一种全排列压缩成一个整数,它的实质是计算当前排列在所有由小到大全排列中的名次,因此是可逆的,称为逆康托展开(这篇博客不会讲)。

    基本原理

    我们用a[i]表示位于位置i后面的数小于A[i]值的个数。
    公式长这样:

    (X=sum_{i=1}^n a[i]×(n-i)!+1)
    也就是:
    (X = A[0] × (n-1)! + A[1] × (n-2)! + … + A[n-1] × 0!)
    这个算出来的数康拖展开值,是在所有排列次序 - 1的值,因此X+1即为在全排列中的次序,最终输出的应该是X+1
    举个栗子:
    在(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值。
    带入上面的公式
    X=2*4!+2*3!+0*2!+1*1!+0*0! =>X=61
    最后的结果也就是62.

    康托展开基础-核心代码

    //返回数组a中当下顺序的康托
    int cantor(int *a,int n)
    {
    	int ans=0;
    	for(int i=0;i<n;i++)
    	{
    		int x=0,c=1,m=1;
    		for(int j=i+1;j<n;j++)
    		{
    			if(a[j]<a[i])x++;
    			m*=c;
    			c++;
    		}
    		ans+=x*m;
    	}
    	return ans;
    }
    

    注意:这个函数返回的是X,输出请输出X+1,理由上面说过了.

    康托展开的优化

    很明显,上面讲的方法很暴力 需要(O(n^2))的时间才能跑出来。
    怎么优化呢?
    首先我们可以了解,对于第(i)个位置,若该位置的数是未出现在之前位置上的数中第(k)大的,那么有((k-1) imes (N-i)!)种方案是该位置上比这个排列小的,所以总排名比该排列小。
    由此我们可以得到,该排列的排名等于(sum_{i=1}^{N}(a[a_i]-1) imes (N-i)!)
    p.s.内层(a_i)表示给出的排列中的第i个数,重名不要管因为上文就是用的a数组计排名
    阶乘问题因为每次都来计算太费事,所以预处理一下就可以了。
    根据思路就可以用树状数组水过去模板了
    不会树状数组的向这里看

    代码:

    #include<bits/stdc++.h>
    #define FAST_IN std::ios::sync_with_stdio(false);cin.tie(NULL);
    #define MOD 998244353
    using namespace std;
    long long fac[1000010],a,n,tree[1000001],ans;
    int lowbit(int k)
    {
    	return k&-k;
    }
    long long ask(long long s)
    {
        long long ans=0;
        for(long long i=s;i>=1;i-=lowbit(i))
    		ans+=tree[i];
        return ans;
    }
    void add(int s,int num)
    {
    	for(long long i=s;i<=n;i+=lowbit(i))
    		tree[i]+=num;
    }
    void cal()
    {
    	fac[0]=1;
    	for(int i=1;i<=n;i++)
    	{ 
    		fac[i]=fac[i-1]*i%MOD;
    		add(i,1);
    	}
    }
    int main()
    {
    	FAST_IN;
    	cin>>n;
    	cal();
    	for(int i=1;i<=n;i++)
    	{ 
    		cin>>a;
    		ans=(ans+(ask(a)-1)*fac[n-i]%MOD)%MOD;
    		add(a,-1);
    	}
    	cout<<ans+1<<endl;
    	return 0;
    }
    

    模板题传送门

    ov.

    个人博客地址: www.moyujiang.com 或 moyujiang.top
  • 相关阅读:
    使用binlog日志还原数据详解
    js自定义双击函数
    部署全局ajax处理
    mysql开启binlog日志和慢查询日志
    iOS-静态库,动态库,framework浅析(三)
    iOS-静态库,动态库,framework浅析(二)
    ios-静态库,动态库,framework浅析(一)
    iOS
    xcode工程编译错误:The maximum number of apps for free development profiles has been reached.
    iOS
  • 原文地址:https://www.cnblogs.com/moyujiang/p/11233080.html
Copyright © 2011-2022 走看看