题解:P11319 [NOISG2020 Qualification] Cryptography
康托展开
给大家一个式子,这个式子就是康托展开的模版。
\(rank = 1 + \sum_{i=1}^{n} a_n \times (n-i)!\)
然后我们对这个排列 \(P\) 进行离散化,最后直接来个康托展开的模版就行了。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxm = 1000006;
const int mod = 1e9 + 7;
typedef long long ll;
ll fac[maxm], c[maxm], pos[maxm], n, ans = 1;
// 因为计算排名,所以 ans 初始为 1
// c 为树状数组
struct node {
ll id, x;
}a[maxm];
bool cmp(node x, node y) {
return x.x < y.x;
}
ll lowbit(ll x) {
return x & -x;
}
ll sum(ll x) { // 求和
ll ans = 0;
while (x > 0) {
ans += c[x] % mod;
x -= lowbit(x);
}
return ans % mod;
}
void add(ll x, ll k) { // 增加 k
while (x <= n) {
c[x] += k;
x += lowbit(x);
}
}
int main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i].x), a[i].id = i;
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++)
pos[a[i].id] = i; // 离散化,使数值减小并且更加清楚
fac[0] = 1; // fac 计算阶乘
for (int i = 1; i <= n; i++) {
fac[i] = fac[i-1] * i % mod;
fac[i] %= mod;
}
// 康托展开模版
for (int i = 1; i <= n; i++) add(i, 1);
for (int i = 1; i <= n; i++) {
ans += ((sum(pos[i]) - 1) * fac[n - i] % mod) % mod;
add(pos[i], -1);
}
printf("%lld\n", ans % mod);
return 0;
}