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;	
}
posted @ 2019-10-25 21:52  四季夏目天下第一  阅读(249)  评论(1编辑  收藏  举报