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

    康托展开

    公式

    \[ans=1+\sum_{i=1}^{n} A[i]\times(n-i)! \]

    原理

    如我想知道321是{1,2,3}中第几个小的数可以这样考虑 :

    第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2× 2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1× 1!=1 所以小于321的{1,2,3}排列数有2×2!+1×1!=5个。所以321是第6个小的数。 2 ×2!+1× 1!+0× 0!就是康托展开。

    再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0× 3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2,1×2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0×1! ,所以比1324小的排列有0×3!+1× 2!+0×1 !=2个,1324是第三个小数。(摘自百度)

    用处

    洛谷模板题是用于求某排列的排名。-->
    传送门

    此题要求:给出一个排列求他在全排列中的排名。 直接套式子。

    打暴力的复杂度是 $ o(n^2) $,水不过去。

    于是我们想到了用树状数组,具体写法还是看我以前写的一篇博客-->传送门

    先预处理阶乘,用数组存,o(n),然后用类似于权值线段树的树状数组来节省时间来判重。

    上代码

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=998244353;
    template <typename T>
    inline void read(T &x) {
        x = 0;
        int f = 1;
        char ch = getchar();
        while (!isdigit(ch)) {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (isdigit(ch)) {
            x = x * 10 + (ch ^ 48);
            ch = getchar();
        }
        x *= f;
        return;
    }
    template <typename T>
    inline void write(T x){
        if(x < 0) {
            putchar('-');
            x = -x;
        }
        if(x > 9)
            write(x/10);
        putchar(x % 10 + '0');
        return;
    }
    //-------------------------以下是正文
    int q[1000005],n;
    long long ans,fac[1000005];
    inline int lowbit(int x){return x&(-x);}
    inline void update(int x,int y){
        for(int i=x;i<=n;i+=lowbit(i))
            q[i] += y;
    }
    inline int getsum(int a)
    {
        int ans=0;
        for(int i=a;i;i-=lowbit(i))
            ans+=q[i];
        return ans;
    }
    int main(){
    	int x;
        read(n);
        fac[0]=1;
        for(int i=1;i<=n;i++)
        {
        	fac[i]=fac[i-1]*i%mod;
        	update(i,1);//一开始初始化
    	}
    	for(int i=1;i<=n;i++)
    	{
    		read(x);
    		ans=(ans+(getsum(x)-1)%mod*fac[n-i])%mod;
    		update(x,-1);//用一个×一个
    	}
    	write(ans+1);
    	cout<<endl;
    }
    

    还有逆康托展开什么的就懒的写了,反正就是把公式倒着递推嘛。相信各位大佬都是会的。

  • 相关阅读:
    Java中printf
    error C3688: invalid literal suffix '__FSTREXP'; literal operator or literal operator template 'operator ""__FSTREXP' not found
    C# IIS 访问网络映射磁盘 读取文件列表
    datatables 参数详解
    Telnet协议详解
    WCF中的ServiceHost初始化两种方式
    C# FTP 上传、下载、获取文件列表
    Oracle存储过程编译卡死的解决方法
    oracle 查询表结构
    Oracle大数据常见优化查询
  • 原文地址:https://www.cnblogs.com/iloveori/p/12526492.html
Copyright © 2011-2022 走看看