cogs 2287. [HZOI 2015]疯狂的机器人 (NTT优化DP)

来源:互联网 发布:宜家家装设计软件 编辑:程序博客网 时间:2024/05/22 03:18

题目描述

  1. [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步,那么单考虑这两种走法,方案数g[i]Ci/2iCi/2+1i。那么向左向右也是同理。
设一共走了i步那么不考虑不走的情况,总方案数为
f[j]=ji=0g[i]g[ji]Cij
=ji=0g[i]i!g[ji](ji)!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 998244353using 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);}
0 0