洛谷P10868题解

题目大意

对于由 个非负整数组成的序列 ,要进行 次操作,每次操作需选定序列中两个相邻的数,将这两个数的均值放到原序列中这两个数之间的位置,并删除这两个数。易知进行 次操作之后只剩下一个数,求这个数的期望并对 取模。

解题思路

易知即求不同操作顺序下期望值的平均值,得到式子:

其中 表示每种情况的操作完毕后 的修饰系数(即权重),由于系数一定为 为自然数),于是考虑将上式化简,得到:

其中 表示所有情况的 修饰系数的加和。

于是问题转化为如何求 ,枚举可知:

  • 时,
  • 时,
  • 时,
  • 时,
  • 时,

于是将它们写在一起即有:

可以看到非常像杨辉三角,实际上,它也具有与杨辉三角类似的性质:

  • 每一行的首项为 为双阶乘)。
  • 每一行都是左右对称的。
  • 每一行非首项/末项的数都可由它上一行的相邻两个数推出。

例: 等等。

记第 行第 个数为 ,则易得递推公式:

根据 可求出通项公式:

然而得到这样的结果我们是不满意的,因为暴力计算 个组合数的时间复杂度为 ,于是考虑进行作商,得到:

于是我们可以在线性时间内计算出

几个需要注意的问题:

  • 记得开 long long
  • 逆元开销较大,尽量将分母一次性计算完毕之后再求逆元。
  • 要经常取模,否则会爆()。

附上我的 AC 代码(500 ms 左右):

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include<bits/stdc++.h>

#define debug puts("bug\n")
#define int long long
#define mod 998244353
using namespace std;

int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}

void write(int a) {
if (a < 0) {
putchar('-');
a = -a;
}
if (a > 9) write(a / 10);
putchar(a % 10 + '0');
}

int quick_power(int a, int b, int p) {
int ans = 1;
while (b > 0) {
if (b & 1) {
ans *= a;
ans %= p;
}
a *= a;
a %= p;
b >>= 1;
}
return ans;
}

int inverse(int a, int b) {
return quick_power(a, b - 2, b);
}

vector<int> weight;

int start; // 首项的分子

int down; // 分母

void init_start(int n) {
// 处理第n+1行的首项分子 (2n-1)!!
start = 1;
for (int i = 1; i <= n; i++) {
start *= (2 * i - 1);
start %= mod;
}
}

void init_down(int n) {
// 处理分母 2^n·n!
down = 1;
for (int i = n; i >= 1; i--) {
down *= i;
down %= mod;
down <<= 1;
down %= mod;
}
}

void init(int n) {
// 处理第n+1行 递推系数 (n+1-i)/i · (2i-1)/(2n-2i+1)
weight.emplace_back(start);
for (int i = 1; i <= n; i++) {
int temp = weight[i - 1] * (n - i + 1) % mod;
temp = temp * (2 * i - 1) % mod;
int now = (2 * n - 2 * i + 1) * i % mod;
now = inverse(now, mod);
temp = temp * now % mod;
weight.emplace_back(temp);
}
}

int sum;
int ans;

signed main() {
int n = read();
init_start(n - 1);
init_down(n - 1);
init(n - 1);
for (int i = 1; i <= n; i++) {
int temp = read();
sum += (temp * weight[i - 1]) % mod;
sum %= mod;
}
ans = inverse(down, mod);
ans = (sum * ans) % mod;
write(ans);
return 0;
}

当你回首你曾经走过的路时,你会发现,这几年的付出,虽有艰辛,却换来了成长和进步。


洛谷P10868题解
https://lngym.top/刷题/洛谷P10868题解/
作者
lngym
发布于
2024年8月12日
许可协议