这应该是数学吧?
康托展开要求的就是解决排列问题的一种数学,具体解决的是一组数的一个合法排列在这组数的全部排列中的排名应该就只能解决这个吧?
它的解决方式也挺简单的,有一个比较方便的公式 $$ans = \sum_{i = 1}^{n} sum_{ai} * (n -
i)!$$
感性理解一下:
比如有一个序列 \(2 , 4 , 1 , 5 , 3\) , 它是序列 \(1 , 2 , 3 , 4 , 5\)的一组合法排列,要求它在其所有合法排列中的排名,该怎样求嘞?
- 第一位是 \(2\) ,比它小的只有 \(1\) ,而它后面还有 \(4\) 位,所以总方案数就是 \(1 * 4 !\) 。
- 第二位是 \(4\) ,比它小的有 \(1 , 2 , 3\) 但是 \(2\) 在前面已经用过了,所以合法的有 \(2\) 位,而它后面有 \(3\) 位,所以总方案数就是 \(2 * 3 !\) 。
- 第三位是 \(1\) ,没有比它小的,所以总方案数就是 \(0\) 。
- 第四位是 \(5\) ,比它小的有 \(1 , 2 , 3 , 4\) ,但是前面有 \(1 , 2 , 4\) 了,所以合法的有 \(1\) 位,而它后面有 \(1\) 位,所以总方案数就是 \(1 * 1 !\) 。
- 第五位后面没有位数了所以总方案数是 \(0\) 。
所以总方案数就是 \(1 * 4 ! + 2 * 3 ! + 0 + 1 * 1 ! + 0 = 36\) ,但是需要注意奥,这是它前面的,要求它自己的位置的话还要加一(因为它自己也算一个)。
洛谷上有一道板子题,可以顺便切掉它但是时间卡的有点紧,所以要用线段树或者树状数组优化 康托展开 。
下面是代码
#include <iostream>
using namespace std;
const int Mod = 998244353;
const int Max = 1000010;
long long n , ans;
long long t[4*Max] , a[Max] , x[Max];
// t是树状数组 , a是输入数组 , x是阶乘数组
long long lowbit(int x) {
return x & (-x);
}
void add(int x , int k) {
for(int i = x; i <= n; i += lowbit(i)) t[i] += k;
}
long long query(int x) {
long long res = 0;
for(int i = x; i >= 1; i -= lowbit(i)) res += t[i];
return res;
}
signed main() {
cin >> n;
x[0] = x[1] = 1;
for(long long i = 1; i <= n; i++) {
x[i] = (x[i-1] * i) % Mod; // 阶乘
add(i , 1); // 树状数组初始化
}
for(long long i = 1; i <= n; i++) {
cin >> a[i];
ans = (ans + ((query(a[i]) - 1) * x[n-i]) % Mod) % Mod; // 这就是上面讲的康托展开
add(a[i] , -1); // 还原树状数组,之前是1,还原的话就是-1喽
}
cout << ans + 1; // 记得加上自己的那一位哦
return 0;
}