cogs2287(组合数学+画柿子+NTT)

来源:互联网 发布:winaip软件的使用 编辑:程序博客网 时间:2024/04/29 16:51

cogs都可以下数据的题,bzoj居然是权限题。
在某犇博客找到的,即使知道是ntt,刚看到时还是一脸懵逼,完全没头绪,所以就做了。

题面
题意:某机器人一开始在(0,0),每一次操作可以向右,向左,向下,向上走或不走。不可以在某一次操作后坐标为负数。问n次操作,最后回到(0,0)的方案数,模一个费马素数。

假设不可以不走,走n步(n为偶数),且只有一维,那就是一个卡特兰数。我依旧百度了一下,发现公式为

Cn/2nCn/21n
设为g[n]。

考虑两维的走n步的方案数,设为f[n],依然不可以不走,就有

f[n]=i=0ng[i]g[ni]Cin

看起来像个卷积,把组合数拆出来,有

f[n]=n!i=0ng[i]i!g[ni](ni)!
这就是个卷积了,上ntt就可以了。

考虑可以不走的情况,我们枚举走了i步,不走(n-i)步,有

ans=i=0nf[i]Cin

我觉得画柿子还是要回归本源,比如说不要追求卡特兰数的递推之类的。次方,阶乘始终是最好求的东西。

#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=400100;const LL p=998244353,g=3;int n,rev[N];LL cheng(LL a,LL b){    LL res=1ll;    for(;b;b>>=1,a=a*a%p)    if(b&1)    res=res*a%p;    return res;}void init(int lim){    int k=-1;    n=1;    while(n<=lim)    k++,n<<=1;    for(int i=0;i<n;i++)    rev[i]=(rev[i>>1]>>1) | ((i&1)<<k);}void ntt(LL *a,bool ops){    for(int i=0;i<n;i++)    if(i<rev[i])    swap(a[i],a[rev[i]]);    for(int l=2;l<=n;l<<=1)    {        int m=(l>>1);        LL wn;        if(ops)        wn=cheng(g,(p-1)/l);        else        wn=cheng(g,p-1-(p-1)/l);        for(int i=0;i<n;i+=l)        {            LL w=1ll;            for(int k=0;k<m;k++)            {                LL t=a[i+k+m]*w%p;                a[i+k+m]=(a[i+k]-t+p)%p;                a[i+k]=(a[i+k]+t)%p;                w=w*wn%p;            }        }    }    if(!ops)    {        LL Inv=cheng(n,p-2);        for(int i=0;i<n;i++)        a[i]=a[i]*Inv%p;    }}int nn;LL jc[N],ijc[N];LL f[N],ans;int main(){    jc[0]=jc[1]=1ll;    for(LL i=2;i<N;i++)    jc[i]=jc[i-1]*i%p;    for(LL i=0;i<N;i++)    ijc[i]=cheng(jc[i],p-2);    cin>>nn;    for(int i=1;i<=nn/2;i++)    f[i]=(ijc[i]*ijc[i]%p-ijc[i-1]*ijc[i+1]%p+p)%p;    f[0]=1ll;    init(nn*2);    ntt(f,1);    for(int i=0;i<n;i++)    f[i]=f[i]*f[i]%p;    ntt(f,0);    for(int i=0;i*2<=nn;i++)    ans=(ans+f[i]*jc[nn]%p*ijc[nn-2*i]%p)%p;    cout<<ans<<endl;    return 0;}

我们追究会相知,在那遥远的天穹。

原创粉丝点击