题目链接
题意
定义长度为 n 的序列 a 的最大前缀和为 max(0,maxi=1n∑j=1iaj)。
求对于由 n 个 1,m 个 −1 组成的所有序列的「最大前缀和」之和是多少。
思路
设 fi 为最大前缀和为 i 的序列数量,答案即为 ∑i=0ni⋅fi。
发现较难直接求 fi,于是定义 gi=∑j=infi,差分即可求出 fi。
把每个 1 看成位移向量 (1,1),每个 −1 看成 (1,−1),则题目中要求的所有序列一一对应 (0,0) 到 (n+m,n−m) 的所有路径。
例如,序列 {1,−1,1,1,−1,−1,−1,1} 对应的路径如下:(图转自 Sooke’s Blog)
最大前缀和 ≥i,等价于路径与 y=i 有交点。
下面分两种情况讨论:
- 当路径起点和终点分别位于直线两侧,此时它一定与 y=i 有交,所以 gi=Cnn+m;
- 当路径起点和终点分别位于直线同侧,则将起点关于 y=i 做对称至 (0,2i),路径则一定与直线有交。(还是借 Sooke 大佬的图)
此时路径为从 (0,2i) 至 (n+m,n−m),位移为 (n+m,n−m−2i)。
设序列含 x 个1,y 个 −1。
{x+yx−y=n+m=n−m−2i
解得 {xy=n−i=m+i,此时 gi=Cxx+y=Cn−in+m。
综上所述:
gi={Cnn+m (i≤n−m)Cn−in+m (i>n−m)
实现
预处理出 (n+m)! 以及 n+m 以内的所有数的逆元,O(1) 查询组合数。
注意本题的模数为 998244853。
代码
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
| #include<cstdio> using namespace std; const int N=4000,mod=998244853; int n,m,x,y; long long fac[N+1],inv[N+1],g[N+1],ans; long long pow(long long x,int times) { long long res=1; for(;times;x=x*x%mod,times>>=1) { if(times&1) { res=res*x%mod; } } return res; } void init() { fac[0]=inv[0]=1; for(int i=1;i<=N;++i) { fac[i]=fac[i-1]*i%mod; inv[i]=pow(fac[i],mod-2); } return; } inline long long C(int x,int y) { return x>=y?fac[x]*inv[y]%mod*inv[x-y]%mod:0; } int main() { init(); scanf("%d%d",&n,&m); x=n+m,y=n-m; for(int i=0;i<=n;++i) { g[i]=i<=y?C(x,n):C(x,n-i); } for(int i=1;i<=n;++i) { ans=(ans+(g[i]-g[i+1])*i)%mod; } printf("%lld\n",(ans+mod)%mod); return 0; }
|