洛谷 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;
}
posted @ 2020-01-02 21:25  (o-ωq)).oO  阅读(169)  评论(0编辑  收藏  举报