洛谷 P1641 [SCOI2010]生成字符串
题目
怎么说呢。。。这道sb题我是傻掉惹。。。想得挺麻烦的。。。只能说基础不扎实吧。。。如果要看简洁的题解,请移步xyz32768dalao的博客。
我们可以找出不符合条件的字符串的数量\(num\),将总数减去。
显然,对于一个字符串的前\(k\)个字符,如果已经不满足条件了,那后面的字符就可以随便填了。
为了避免重复计算,我们设\(f_i\)为前\(2i-2\)个字符合法,但前\(2i-1\)个字符不合法的数量,显然\(i\)等于前\(2i-1\)个字符中\(0\)的数量。
故\(num=\sum\limits_{i=1}^m{f_i \cdot \frac{(n+m-2i+1)!}{(n-i+1)!(m-i)!}}\) (使用可重排列公式)
设\(g_i= C_{2i}^{i}\),则\(f_i= C_{2i-1}^{i-1}-\sum\limits_{j=1}^{i-1}{g_jf_{i-j}}\) 可以发现这是一个卷积的形式,所以用分治FFT优化
然后。。。额。。。求出\(f\)的前几项,\(1,1,2,5,14 \cdots\) 发现将\(f\)的下标减\(1\)即为卡特兰数,故预处理阶乘逆元可在\(O(n)\)的复杂度下通过。
完结撒花~(大雾
\({\frak{code:}}\)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e6+3,p=20100403;
int n,m;
LL f[N],fac[N],inv[N],ans;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL LL ksm(LL a,LL b){
LL c=1;
while(b){
if(b&1) c=c*a%p;
a=a*a%p,b>>=1;
}
return c;
}
void exgcd(LL a,LL b,LL &x,LL &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
IL LL C(LL a,LL b){return fac[a]*inv[b]%p*inv[a-b]%p;}
int main()
{
LL x,y,z;
n=in(),m=in();
fac[0]=inv[0]=f[1]=1;
for(int i=1;i<=n+m;++i) fac[i]=fac[i-1]*i%p;
exgcd(fac[n+m],p,inv[n+m],x);
inv[n+m]=(inv[n+m]%p+p)%p;
for(int i=n+m-1;i;--i) inv[i]=inv[i+1]*(i+1)%p;
for(int i=2;i<=m;++i) f[i]=C(2*(i-1),i-1)*fac[i-1]%p*inv[i]%p;
for(int i=1;i<=m;++i) ans=(ans+f[i]*fac[n+m-i*2+1]%p*inv[n-i+1]%p*inv[m-i])%p;
ans=(fac[n+m]*inv[n]%p*inv[m]%p-ans+p)%p;
printf("%lld\n",ans);
return 0;
}