题目描述
- [HZOI 2015]疯狂的机器人
★★★ 输入文件:crazy_robot.in 输出文件:crazy_robot.out 简单对比
时间限制:1 s 内存限制:512 MB
【题目描述】
现在在二维平面内原点上有一只机器人
他每次操作可以选择向右走,向左走,向下走,向上走和不走(每次如果走只能走一格)
但是由于本蒟蒻施展的大魔法,机器人不能走到横坐标是负数或者纵坐标是负数的点上
否则他就会big bang
给定操作次数n,求有多少种不同的操作序列使得机器人在操作后会回到原点
输出答案模998244353后的结果
注意如果两个操作序列存在某一时刻操作不同,则我们认为这两个操作序列不同
【输入格式】
输入n,表示操作次数
n<=100000
【输出格式】
按要求输出答案
【样例输入】
3
【样例输出】
7
【提示】
样例解释:
机器人有7种操作序列
1、不走 不走 不走
2、不走 向右 向左
3、向右 不走 向左
4、向右 向左 不走
5、不走 向上 向下
6、向上 不走 向下
7、向上 向下 不走
题解
因为最终要回到原点,所以向上和向下的步数相等,向左和向右的步数相等。
需要保证在行走的过程中,不能走到负数区域,那么就是在单独看向上和向下两种走法时,任意时刻向上走的步数大于等于向下走的步数,向左和向右也是同理。
这是一个经典的卡特兰数问题,如果向上和向下一共走了
i
步,那么单考虑这两种走法,方案数
设一共走了i步那么不考虑不走的情况,总方案数为
f[j]=∑ji=0g[i]∗g[j−i]∗Cij
=∑ji=0g[i]i!∗g[j−i](j−i)!∗j!
然后发现是卷积的形式,由于998244353是费马质数,所以我可以用NTT来加速。
最后我们只需要考虑上原地不动的就可以
ans=∑i=0nf[i]∗Cin
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 500030
#define LL long long
#define p 998244353
using namespace std;
int n,n1,m,L;
LL f[N],g[N],jc[N],R[N];
LL quickpow(LL num,LL x)
{
LL base=num%p; LL ans=1;
while (x) {
if (x&1) ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans;
}
LL calc(LL n,LL m)
{
if (n<m) return 0;
return jc[n]*quickpow(jc[n-m]*jc[m]%p,p-2)%p;
}
void NTT(LL x1[N],int n,int opt)
{
int j;
for (int i=0;i<n;i++)
if (i<R[i]) swap(x1[i],x1[R[i]]);
for (int i=1;i<n;i<<=1) {
LL wn=quickpow(3,(p-1)/(i<<1));
for (int p1=i<<1,j=0;j<n;j+=p1) {
LL w=1;
for (int k=0;k<i;k++,w=(w*wn)%p) {
LL x=x1[j+k],y=(w*x1[j+k+i])%p;
x1[j+k]=(x+y)%p; x1[j+k+i]=(x-y+p)%p;
}
}
}
if (opt==-1) reverse(x1+1,x1+n);
}
int main()
{
freopen("crazy_robot.in","r",stdin);
freopen("crazy_robot.out","w",stdout);
scanf("%d",&n);
jc[0]=1;
for (int i=1;i<=n;i++) jc[i]=(jc[i-1]*(LL)i)%p;
for (int i=1;i<=n;i++){
if (!(i&1)) f[i]=calc(i,i/2)-calc(i,i/2+1);
f[i]=(f[i]+p)%p;
}
f[0]=1;
for (int i=0;i<=n;i++) f[i]=f[i]*quickpow(jc[i],p-2)%p;
m=n*2; n1=0;
for (n1=1;n1<=m;n1<<=1) L++;
for (int i=0;i<=n1;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
NTT(f,n1,1); //cout<<n1<<endl;
for (int i=0;i<=n1;i++) g[i]=f[i];
for (int i=0;i<=n1;i++) f[i]=(f[i]*g[i])%p;
NTT(f,n1,-1);
LL inv=quickpow(n1,p-2);
for (int i=0;i<=n1;i++) f[i]=f[i]*inv%p;
for (int i=0;i<=n1;i++) f[i]=f[i]*jc[i]%p;
LL ans=0;
for (int i=0;i<=n;i++)
ans=(ans+f[i]*calc(n,i)%p)%p;
printf("%I64d\n",ans);
}