CodeForces

题意:求n个1,m个-1组成的所有序列中,最大前缀之和。

首先引出这样一个问题:使用n个左括号和m个右括号,组成的合法的括号匹配(每个右括号都有对应的左括号和它匹配)的数目是多少?

1.当n=m时,显然答案为卡特兰数$C_{2n}^{n}-C_{2n}^{n+1}$

2.当n<m时,无论如何都不合法,答案为0

3.当n>m时,答案为$C_{n+m}^{n}-C_{n+m}^{n+1}$,这是一个推论,证明过程有点抽象,方法是把不合法的方案数等价于从(0,-2)移动到(n+m,n-m)的方案数,详见https://blog.csdn.net/x_1023/article/details/78290683

回到题目,如果把1看成右括号,把-1看成左括号,那么最大前缀和为0相当于匹配合法,就是上面讨论的第三种情况。

如果进一步扩展,最大前缀和为1,2,3,...,k的情况该如何处理呢?

考虑最大前缀和大于等于k的情况,其实根据上面的方法,可以等价于从点(0,-2k)走到点(n+m,n-m)的方案数,即$C_{n+m}^{n+k}$,前提是$max(m-n,0)leqslant kleqslant m$,然后差分一下就能得到最大前缀和等于k时的方案数了。复杂度$O(n+m)$。由于题目中的n和m分别代表右括号和左括号,所以n和m要反过来。

自己的组合数学真是太辣鸡了,还是要提高一下姿势水平~

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=4000+10,mod=998244853;
 5 int n,m,inv[N],f[N],invf[N],ans[N];
 6 int C(int n,int m) {return n<m?0:(ll)f[n]*invf[m]%mod*invf[n-m]%mod;}
 7 int main() {
 8     inv[1]=f[0]=invf[0]=1;
 9     for(int i=2; i<N; ++i)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
10     for(int i=1; i<N; ++i)f[i]=(ll)f[i-1]*i%mod,invf[i]=(ll)invf[i-1]*inv[i]%mod;
11     scanf("%d%d",&n,&m);
12     for(int i=max(n-m,0); i<=n; ++i)ans[i]=C(n+m,m+i);
13     for(int i=max(n-m,0); i<n; ++i)ans[i]=(ans[i]-ans[i+1]+mod)%mod;
14     int sum=0;
15     for(int i=max(n-m,0); i<=n; ++i)sum=(sum+(ll)i*ans[i]%mod)%mod;
16     printf("%d
",sum);
17     return 0;
18 }
原文地址:https://www.cnblogs.com/asdfsag/p/11666246.html