牛客小白月赛60 F-被抓住的小竹

题目链接

F-被抓住的小竹

思路

令 $sum(p)=\sum\nolimits_{x=1}^n\sum\nolimits_{i=1}^x\sum\nolimits_{j=x}^nF(p,i,j,x)$ ,交换求和顺序,得到: $$ sum(p)=\sum\nolimits_{1\le i\le x\le j\le n}F(p,i,j,x)=\sum_{i=1}^n\sum_{j=i}^n\sum_{x=i}^jF(p,i,j,x) $$ 其意为:前两个求和符号一个固定的区间 $[i,j]$ ,最后一个求和符号计算区间中大于等于 $p_x$ 的数量之和,令 $len=j-i+1$,通过模拟可以发现最和一个求和符号的结果是 $1+2+\cdots+len=len\times(len+1)/2$ 。而长度为 $len$ 的区间在长度为 $n$ 的排列中有 $n-len+1$ 个,因此 $sum(p)$ 可以进一步化简为: $$ sum(p)=\sum_{len=1}^n(n-len+1)\times\frac{len\times(len+1)}{2} $$ 给定一个 $n$,$sum(p)$ 的值可以遍历 $len$ 在 $O(n)$ 的时间内求出,符合题目的时间限制,也可以通过平方和公式与立方和公式进一步化简为 $sum(p)=C_{n+3}^4$ 。

接下来考虑 $inv(p)$ 。对于两个不同的数 $x$ 和 $y$ ,它们成为逆序对的概率为 $\frac{1}{2}$ ( $x$ 要么在 $y$ 之前,要么在 $y$ 之后),而 $n$ 个数有 $C_{n}^2$ 个二元组,因此对于一个排列,其逆序对的个数期望是 $C_n^2/2$ ,也即 $inv(p)$ 。更详细的证明在全排列的逆序数和

综上,题目最后所求公式: $$ \sum\nolimits_{p\in{S_p}}H(p)=n!\times\frac{C_n^2}{2}\times\sum_{len=1}^n(n-len+1)\times\frac{len\times(len+1)}{2} $$

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "iostream"
#include "vector"
#include "string"
#include "utility"
#include "queue"
#include "set"
#include "map"
#include "unordered_map"
#include "algorithm"
#include "cstring"
#include "stack"
#include "cmath"
#include "numeric"
#include "cassert"

using namespace std;

typedef long long ll;
const int N = 1e5 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1e9 + 7;
ll c[N], fact[N];

void init() {
    fact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = fact[i - 1] * i % MOD;
        c[i] = 1LL * i * (i - 1) / 2 % MOD;
    }
}

ll get(int n) {
    ll cur = 0;
    for (int i = 1; i <= n; i++) {
        cur = (cur + 1LL * i * (i + 1) / 2 % MOD * (n - i + 1)) % MOD;
    }
    return cur;
}

ll quick_mod(ll x, ll n) {
    x %= MOD;
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    init();
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        ll ans = fact[n] * c[n] % MOD * get(n) % MOD * quick_mod(2, MOD - 2) % MOD;
        cout << ans << "\n";
    }

    return 0;
}
updatedupdated2022-11-122022-11-12