康托展开

康托展开

题目描述

\(1\sim N\) 的一个给定全排列在所有 \(1\sim N\) 全排列中的排名。结果对 \(998244353\) 取模。


设全排列\(A = [3,2,5,4,1]\)
第1位3,比\(3\)小的数有\(2,1\)\(2 \times 5!\)
第2位2,比二小的数为\(1\),已经被处理过了。\(0 \times 4!\)
第3位5,比5小的数有\(1,2,3,4\)由于前面已经处理过\(1,2\)\(2 \times 3!\)
第4位4,比4小的数有\(1,2,3\)\(0 \times 2!\)
第5位1,比1小的数为\(0\)个。
\(S = 2 \times 4! + 0 \times 3! + 2 \times 2! + 0 \times 1\)

也就是求出前\(n\)位与这个排列相同的,而这一位比\(x\)小的排列数。
用树状数组维护\(a_i\)的前缀和即可

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1e6 + 5;
const ll p =  998244353;

int n;
ll fact[N],trees[N];

void add(int x,int k)
{
    for(; x <= n; x+=x&-x )trees[x] += k;
}

ll ask(int x)
{
    ll  res = 0;
    for(;x ;x-=x&-x) res += trees[x];
    return res;
}

int main()
{
    cin >> n;

    fact[0] = 1;
    for(int i = 1 ; i <= n ; i ++ )
    {
        fact[i] = (fact[i - 1] * i) % p;
        add(i,1);
    }

    ll ans = 0;
    for(int a,i = 1 ; i <= n ; i ++ )
    {
        scanf("%d",&a);
        ans = (ans + (ask(a)- 1) * fact[n - i]%p)%p;
        add(a,-1);
    }
    printf("%lld",ans + 1);
    return 0;

}
posted @ 2022-07-17 18:30  Erfu  阅读(29)  评论(0编辑  收藏  举报