康托展开

这应该是数学吧?

康托展开要求的就是解决排列问题的一种数学,具体解决的是一组数的一个合法排列在这组数的全部排列中的排名

它的解决方式也挺简单的,有一个比较方便的公式 $$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是阶乘数组

int 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(int i = 1; i <= n; i++) {
		x[i] = (x[i-1] * i) % Mod; // 阶乘
		add(i , 1); // 树状数组初始化
	}
	for(int 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;
}

BYE~

posted @ 2023-07-22 16:46  Rose_Lu  阅读(37)  评论(1)    收藏  举报