康托展开

Cantor Expansion:

\(X = \sum_{i=1}^n (n-i)!\sum_{j=i+1}^{n}[p_i>p_j]\)

It is a bijection from a permutation of \(p[1..n]\) to its dictionary order.
We can get \((n-1)!\) by pre-calc. And use Tree array to access \(\sum_{j=i+1}^n[p_i>p_j]\)

int a[N];
for(int i = 1; i <= n; ++i){
    add(p[i], 1);
    a[i] = p[i] - query(p[i]);
}

就是说: 求从\(i\)位开始向后的逆序\((p_i, p_j)\), \(j\) from \(i+1\) to \(n\)的个数, 即 求 \(i\) 位之前比 \(p[i]\)小的有几个, 用\(p[i]\)减去它就好. 这很容易用树状数组维护.

Then, a[i] = \(\sum_{j=i+1}^n[p_i>p_j]\)

The entire code:[Luogu5367] 求康托展开对orz取模.

#include<cstdio>
typedef long long ll;
const ll orz = 998244353;
const int N = 1e6 + 1;
int t[N], n;
ll fac[N];
inline int lowbit(int x) {
	return x & -x;
}
inline int query(int r) {
	int ans = 0;
	for (int i = r; i > 0; i-=lowbit(i)) 
		ans += t[i];
	return ans;
}
inline void add(int p, int x) {
	for (int i = p; i <= n; i+=lowbit(i))
		t[i] += x;
}
inline void pre_fac() {
	fac[0] = 1;
	for (int i = 1; i <= n; ++i)
		fac[i] = fac[i - 1] * i % orz;
}
int main() {
	scanf("%d", &n);
	pre_fac();
	ll ans = 1;
	for (int i = 1; i <= n; ++i) {
		int xi;
		scanf("%d", &xi);
		add(xi, 1);
		int ai = xi - query(xi);
		ans = (ans + fac[n - i] * ai) % orz;
	}
	printf("%lld", ans);
}

逆运算

(待更)咕咕咕...

posted @ 2021-03-04 17:55  _dwt  阅读(14)  评论(0)    收藏  举报