康托展开
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);
}
逆运算
(待更)咕咕咕...