康托展开
康托展开
题目描述
求 \(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;
}
“风雪越是呼啸,雪莲越是绽放”