题目大意
对于由 个非负整数组成的序列 ,要进行 次操作,每次操作需选定序列中两个相邻的数,将这两个数的均值放到原序列中这两个数之间的位置,并删除这两个数。易知进行 次操作之后只剩下一个数,求这个数的期望并对 取模。
解题思路
易知即求不同操作顺序下期望值的平均值,得到式子:
其中 表示每种情况的操作完毕后 的修饰系数(即权重),由于系数一定为 ( 为自然数),于是考虑将上式化简,得到:
其中 表示所有情况的 修饰系数的加和。
于是问题转化为如何求 ,枚举可知:
- 当 时,。
- 当 时,,。
- 当 时,,,。
- 当 时,,,,。
- 当 时,,,,,。
于是将它们写在一起即有:
可以看到非常像杨辉三角,实际上,它也具有与杨辉三角类似的性质:
- 每一行的首项为 ( 为双阶乘)。
- 每一行都是左右对称的。
- 每一行非首项/末项的数都可由它上一行的相邻两个数推出。
例:, 等等。
记第 行第 个数为 ,则易得递推公式:
根据 , 可求出通项公式:
然而得到这样的结果我们是不满意的,因为暴力计算 个组合数的时间复杂度为 ,于是考虑进行作商,得到:
于是我们可以在线性时间内计算出 。
几个需要注意的问题:
- 记得开
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) { start = 1; for (int i = 1; i <= n; i++) { start *= (2 * i - 1); start %= mod; } }
void init_down(int n) { down = 1; for (int i = n; i >= 1; i--) { down *= i; down %= mod; down <<= 1; down %= mod; } }
void init(int n) { 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; }
|
当你回首你曾经走过的路时,你会发现,这几年的付出,虽有艰辛,却换来了成长和进步。