题目大意:
一段长 (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;
}