【[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); }