【SSL 1481】 最大前缀和

题目大意:

一段长 \(n+m\) 的序列,其中 \(n\) 个是 1,\(m\) 个是 0。对序列排列求它所有前缀和的最大值的和。

正文:

如图,其实我们就是要从点 \((0,0)\) 到点 \((n+m,n-m)\)\(x\) 轴为走的步数,\(y\) 轴为最大前缀和。假设我们强制到 \(y\) 轴的 \(y_2\),设 \(y_1\) 为终点即 \(n-m\)

经过 \(y_2\) 的总方案为 \(C_{n+m}^{(n+m)-(y_2-y_1)}\),运用前缀和思想,那只经过 \(y_2\) 不经过 \(y_2+1\) 的方案数就是\(y_2 + 1\) 的减去 \(y_2\) 的。

代码:

ll pow(ll a, int b)
{
    a %= p;
	ll ans = 1;
	for(; b; b >>= 1, a = a * a % p)
		if(b & 1)
			ans = ans * a % p;
    return ans;
}

inline void _init(int n, int m)
{
	prod[0] = inv[1] = 1;
	for (register int i = 1; i <= m; i++)
		prod[i] = (prod[i - 1] * i) % p;
	inv[n + m] = pow(prod[n + m], p - 2);
	for (register int i = n + m - 1; i > 1; --i)
		inv[i] = inv[i + 1] * (i + 1) % p;
}

inline ll C(ll n,ll m){
    if(m > n)return 0;
    return (prod[n]*inv[m] % p * inv[n - m] % p);
}

int main()
{
	scanf ("%lld%lld", &n, &m);
	_init(0, n + m);
	ll pre = 1;
	ans = n;
	for (register int i = n - 1; i >= n - m; --i)
	{
		ll s = (C(n + m, (n - i) % p) - pre + p) % p;
		pre = (s + pre) % p;
		if(i > 0) ans = (ans + s * i % p) % p;
		else break;
	}
	printf("%lld", ans);
	return 0;
}

posted @ 2020-08-15 07:53  Jayun  阅读(178)  评论(0编辑  收藏  举报