【[HZOI 2015]疯狂的机器人】NTT+卡特兰数+组合

这道题复习了很多知识:如卡特兰数,NTT,组合数,乘法逆元,还是不错的。 COGS链接:http://www.cogs.pro:8080/cogs/problem/problem.php?pid=2287 关于卡特兰数:http://www.cppblog.com/MiYu/archive/2010/08/07/122573.html 卡特兰数表:http://www.newuser.top/2018/03/20/%e5%8d%a1%e7%89%b9%e5%85%b0%e6%95%b0%e7%9a%84%e5%ad%a6%e4%b9%a0/ 关于乘法逆元:http://www.newuser.top/2018/03/20/%e4%b8%80%e4%ba%9b%e6%b1%82%e4%b9%98%e6%b3%95%e9%80%86%e5%85%83%e7%9a%84%e7%ae%97%e6%b3%95%e6%80%bb%e7%bb%93/

【题目描述】

现在在二维平面内原点上有一只机器人 他每次操作可以选择向右走,向左走,向下走,向上走和不走(每次如果走只能走一格) 但是由于本蒟蒻施展的大魔法,机器人不能走到横坐标是负数或者纵坐标是负数的点上 否则他就会big bang 给定操作次数n,求有多少种不同的操作序列使得机器人在操作后会回到原点 输出答案模998244353后的结果 注意如果两个操作序列存在某一时刻操作不同,则我们认为这两个操作序列不同

【输入格式】

输入n,表示操作次数 n<=100000

【输出格式】

按要求输出答案

【样例输入】

3

【样例输出】

7

【提示】

样例解释: 机器人有7种操作序列 1、不走 不走 不走 2、不走 向右 向左 3、向右 不走 向左 4、向右 向左 不走 5、不走 向上 向下 6、向上 不走 向下 7、向上 向下 不走 //[latex]C(x,y) 表示从x中选y个的组合数 因为我们只能在正数范围内行走,且最后一定要回到原点,那么一个上必然对应一个下,一个右必然对应一个左,那么如果单方面考虑向上和向下或者单方面考虑向左和向右,那么这也就是一个很显然的卡特兰数的板子,对于每一个 [latex]g[n][/latex],其方案数就是卡特兰数的[latex]C(2n,n) - C(2n,n-1)[/latex]。 现在由于我们要分配到上下和左右去,在不考虑不走这种情况下,f[i]是在走i步的方案数[latex]f[i]=sigma(g[j]*g[i-j]*C(i,j))[/latex]。 这个式子我们可以最后转化为[latex] f[i]=i!*sigma( ( g[j]/j!) *(g[i-j]/(i-j)!) ) [/latex]。 我们看见这个模数好呀,直接NTT卷起来乱搞呀 。 完了之后之后咱们搞出不会动的家伙的方案数就可以了。 Ans = [latex] sigma( f[i] * C(n,i) ) 感觉是不是很简单? 但是那个C(n,i)每次怎么快速搞?我们每一次还找一遍逆元 x!的逆元吗? 关于x!在质数下的逆元求法在乘法逆元总结的文章里已经说清(文章开头),这里简单提一下,我们只需要求出n!的逆元之后,(n-1)!的逆元也就是n!的逆元乘以n就可以得到了,具体证明见文内,这样就可以快速算出一个组合数值了。
#iclude<bits/stdc++.h>
using namespace  std;
typedef long long LL;
const LL maxn = 400005;
const LL mod = 998244353;
const LL g = 3;
LL n;
LL Ainv[maxn],jc[maxn];
LL mont(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans = a*ans%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
LL sss;
LL calc(LL a,LL b)
{
	if(a<b) return 0;
	return jc[a]*Ainv[a-b]%mod*Ainv[b]%mod;
}
void getinv(LL x)
{
	jc[0]=1;
	for(LL i=1;i<=x;i++) jc[i]=(jc[i-1]*i)%mod;
	Ainv[x]=mont(jc[x],mod-2);
	for(LL i=x-1;i>=0;i--) Ainv[i]=(Ainv[i+1]*(i+1))%mod; 
}
void ntt(LL *a,LL s,LL dft)
{
	for(LL i=0,j=0;i<s;i++)
	{
		if(i<j) swap(a[i],a[j]);
		for(LL k=s>>1;(j^=k)<k;k>>=1);
	}
	for(LL st=1;st<s;st<<=1)
	{
		LL ngg = mont(g,((mod-1)+dft*(mod-1)/(st<<1))%(mod-1));
		for(LL i=0;i<s;i+=(st<<1))
		{
			LL gg = 1;
			for(LL j=i;j<i+st;j++)
			{
				LL x=a[j]; LL y=a[j+st]*gg;
				a[j]=(x+y)%mod; a[j+st]=((x-y)%mod+mod)%mod;
				gg=gg*ngg%mod;
			}
		}
	} 
	if(dft==1) return;
	LL inv = mont(s,mod-2);
	for(LL i=0;i<s;i++) a[i]=a[i]*inv%mod;
}
LL aa[maxn],bb[maxn],ans;
int main()
{
	scanf("%lld",&n);
	getinv( n*2 );
	for(int i=1;i<=n;i++)
	{
		if(!(i&1))
		{
		aa[i]= ( (calc(i,i/2)-calc(i,i/2-1) )%mod+mod)%mod;
		aa[i]=aa[i]*Ainv[i]%mod;
		}
	}
	aa[0]=1*Ainv[0];
	for(sss=2;sss<=2*n;sss<<=1);
	ntt(aa,sss,1);
	for(LL i=0;i<sss;i++) aa[i]=aa[i]*aa[i]%mod;
	ntt(aa,sss,-1);
	for(LL i=0;i<sss;i++) aa[i]=aa[i]*jc[i]%mod;
	for(LL i=0;i<=n;i++)
	{
		ans = (ans + calc(n,i)*aa[i]%mod)%mod;
	}
	printf("%lld",(ans%mod+mod)%mod);
}
 
posted @ 2018-05-14 10:57  Newuser233  阅读(6)  评论(0)    收藏  举报