• 康托展开


    康托展开和逆康托展开

    康托展开和逆康托展开

    前言

    康托展开和逆康托展开是针对全排列问题的。

    全排列:对于一个长度为 $N$ 的数组 $a$ , 满足 $1≤ai≤N$ 并且 各个元素互不相同。

    数组顺序
    1 2 3 4 51
    1 2 3 5 42
    1 2 5 3 43

    原理

    康托展开的例子

    如果给一个$N=5$ 的数组$[3,2,5,4,1]$

    对于第一个数据3 , 后面比它小的数据有 2 个,所以 以 [1] 、[2] 为开头的全排列,都在它前面,$2*4!$

    对于第二个数据2 , 后面比它小的数据有 1 个,所以 以 [3,1] 为开头的全排列,都在它前面,$1*3!$

    对于第三个数据5,后面比它小的数据有 2 个,所以 以 [3,2,1]、[3,2,4] 开头的全排列,都在它前面,$2*2!$

    对于第四个数据4,后面比它小的数据有 1 个,所以 以 [3,2,5,1] 开头的全排列,都在它前面,$1*1!$

    对于第五个数据1,后面比它小的数据有 0 个,所以没有全排列在它前面,$0$

    $$ 2*4!+1*3!+2*2!+1*1!+0*0!+1 $$

    因为本身的顺序也算在其中,所以额外加1

    代码

    使用数状数组来进行统计 $ai$ 后面有多少比它小的元素。

    #include 
    #include 
    #include 
    using namespace std;
    typedef long long ll;
    const int mod = 998244353;
    const int MAXN = 1e6+5;
    ll fac[MAXN];
    ll node[MAXN];
    inline void Add(int i,int N){
        while(i<=N){
            node[i]-=1;
            i+=i&-i;
        }
    }
    inline ll Sum(int i){
        int s=0;
        while(i){
            s+=node[i];
            i-=i&(-i);
        }
        return s;
    }
    inline void Init(int N){
        ios::sync_with_stdio(false);
        cin.tie(0);
        fac[0]=1;
        memset(node,0,sizeof(node));
        for(ll i=1;i<=1e6;++i){
            fac[i]=(fac[i-1]*i)%mod;
            node[i]=i&-i;
        }
    }
    int main(){
        int N,x;
        ll sum=1;
        cin>>N;
        Init(N);
        for(int i=0;i>x;
            Add(x,N);
            sum+=Sum(x)*fac[N-i-1];
            sum%=mod;
        }
        cout<
    新赛季的开始
  • 相关阅读:
    CCF-201803-3-URL映射(模拟)
    Problem UVA11134-Fabled Rooks(贪心)
    UVA1152-4 Values whose Sum is 0(分块)
    UVA1605-Building for UN(思维)
    基于XMPP的IOS聊天客户端程序(IOS端一)
    基于XMPP的IOS聊天客户端程序(XMPP服务器架构)
    正则表达式在iOS中的运用
    NSUserDefaults 简介,使用 NSUserDefaults 存储自定义对象
    自动无限循环UIScrollView原理
    NSTimeZone
  • 原文地址:https://www.cnblogs.com/VagrantAC/p/12555040.html
Copyright © 2020-2023  润新知