CF1024E maxpsum
题意
给定 \(N\) 个 \(1\) 与 \(M\) 个 \(-1\),求由这 \(N+M\) 个数组成的所有本质不同的序列中最大前缀和的和
其中最大前缀和 \(maxpsum\) 定义为 \(max\{sum_i\}\)(即前缀和数组中的最大值)
解法
虽然是一个套路,但是第一个想到这种转化的人也太屌了吧。。
建立一个平面直角坐标系,其中,选择 \(-1\) 相当于向右移动一个单位,而 \(+1\) 相当于向上移动一个单位(即 \(-1\) 作为 \(x\) 轴,\(+1\) 作为 \(y\) 轴)
那么,从 \((0,0)\) 开始,最终一定会走到 \((m,n)\)
考虑在坐标系中枚举一个 \(b\),这个 \(b\) 是直线 \(l:y=x+b\) 的截距
那么,如果一条从 \((0,0)\) 到 \((m,n)\) 的路径在某一时刻位于 \(l\) 的上方,那么其 \(maxpsum\) 一定大于 \(b\)
设 \(f_i\) 为 \(maxpsum\) 至少为 \(i\) 的序列个数,那么 \(f_i\) 即为从 \((0,0)\) 到 \((m,n)\) 并且在某一时刻位于 \(l\) 上方的路径条数
这个不太好计算,我们再转化一下,取 \((m,n)\) 对于 \(l\) 的对称点 \((n-b,m+b)\),用组合数计算从 \((0,0)\) 到这一点的路径条数。很容易证明这里的每一条路径都唯一对应 \(f_i\)(取一个对称)
那么就可以用一个简单容斥求出 \(maxpsum\) 恰好为 \(i\) 的序列个数了,即 \(f_i-f_{i+1}\)
那么最后的答案即为 \(\sum i\times (f_i-f_{i+1})\)
代码
#include <cstdio>
#include <iostream>
using namespace std;
const int MAX_N = 2e6 + 10;
const int mod = 998244853;
int N, M;
int fac[MAX_N], Ifac[MAX_N];
int f[MAX_N];
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline void inc(int& x, int y) { (x += y) >= mod ? x -= mod : 0; }
int fsp(int x, int y = mod - 2) {
int res = 1;
for (; y; x = mul(x, x), y >>= 1) if (y & 1) res = mul(res, x);
return res;
}
void init() {
int lim = N + M;
fac[0] = 1;
for (int i = 1; i <= lim; ++i) fac[i] = mul(fac[i - 1], i);
Ifac[lim] = fsp(fac[lim]);
for (int i = lim - 1; i >= 0; --i) Ifac[i] = mul(Ifac[i + 1], i + 1);
}
int C(int n, int m) {
return mul(fac[n], mul(Ifac[m], Ifac[n - m]));
}
int calc(int n, int m) { return C(n + m, n); }
int main() {
scanf("%d%d", &N, &M);
init();
int ans = 0;
for (int i = max(1, N - M); i <= N; ++i) f[i] = calc(N - i, M + i);
for (int i = N; i >= max(1, N - M); --i) inc(ans, mul(i, (f[i] - f[i + 1] + mod) % mod));
printf("%d\n", ans);
return 0;
}