• COGS2287 [HZOI 2015]疯狂的机器人


    【题目描述】

    现在在二维平面内原点上有一只机器人

    他每次操作可以选择向右走,向左走,向下走,向上走和不走(每次如果走只能走一格)

    但是由于本蒟蒻施展的大魔法,机器人不能走到横坐标是负数或者纵坐标是负数的点上

    否则他就会big bang

    给定操作次数n,求有多少种不同的操作序列使得机器人在操作后会回到原点

    输出答案模998244353后的结果

    注意如果两个操作序列存在某一时刻操作不同,则我们认为这两个操作序列不同

    【输入格式】

    输入n,表示操作次数

    n<=100000

    【输出格式】

    按要求输出答案

    【样例输入】

    3

    【样例输出】

    7

    【提示】

    样例解释:

    机器人有7种操作序列

    1、不走 不走 不走

    2、不走 向右 向左

    3、向右 不走 向左

    4、向右 向左 不走

    5、不走 向上 向下

    6、向上 不走 向下

    7、向上 向下 不走

    正解:组合数学+$NTT$。

    学习卡特兰数以后做这题好像不难?

    我们把操作分为$3$类,向右和向左为一类,向上和向下为一类,不动为一类。

    那么如果只考虑前两类中的任何一类,那就是卡特兰数,因为这就是要求有$n/2$个$+1$,$-1$,且所有前缀和都$>=0$的序列方案数。很显然,只有偶数能取前两种情况。

    那么我们扩展一下,如果有前两种操作,该怎么做?

    $f[n]$表示$n$步路回到原点的方案数,那么$f[n]=sum_{i=0}^{n}a[i]*a[n-i]*inom{n}{i}$。

    $a[i]$表示前两类操作走$i$步的方案数,$a[i]=c[i/2]$,当且仅当$i$为偶数,$c$为卡特兰数。

    也就是说,从第一类操作中选$i$步,第二类操作中选$n-i$步,最后再组合起来,就是这个方案。

    上式我们已经可以用$NTT$优化,做到$O(nlogn)$的复杂度。

    考虑第$3$中操作,其实很简单了。我们只要枚举前两个操作总共有多少步,再和第$3$个操作组合一下就行。

    也就是$Ans=sum_{i=0}^{n}f[i]*inom{n}{i}$。

     1 #include <bits/stdc++.h>
     2 #define il inline
     3 #define RG register
     4 #define ll long long
     5 #define rhl (998244353)
     6 #define N (500010)
     7 
     8 using namespace std;
     9 
    10 int inv[N],fac[N],ifac[N],a[N],c[N],rev[N],n,m,lg,ans;
    11 
    12 il int qpow(RG int a,RG int b){
    13   RG int ans=1;
    14   while (b){
    15     if (b&1) ans=1LL*ans*a%rhl;
    16     a=1LL*a*a%rhl,b>>=1;
    17   }
    18   return ans;
    19 }
    20 
    21 il void pre(){
    22   fac[0]=ifac[0]=fac[1]=ifac[1]=inv[1]=1;
    23   for (RG int i=2;i<=(n<<1);++i){
    24     inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl;
    25     fac[i]=1LL*fac[i-1]*i%rhl;
    26     ifac[i]=1LL*ifac[i-1]*inv[i]%rhl;
    27   }
    28   return;
    29 }
    30 
    31 il void NTT(int *a,RG int n,RG int f){
    32   for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
    33   for (RG int i=1;i<n;i<<=1){
    34     RG int gn=qpow(3,(rhl-1)/(i<<1)),x,y;
    35     for (RG int j=0,g=1;j<n;j+=i<<1,g=1)
    36       for (RG int k=0;k<i;++k,g=1LL*g*gn%rhl){
    37     x=a[j+k],y=1LL*g*a[j+k+i]%rhl;
    38     a[j+k]=x+y; if (a[j+k]>=rhl) a[j+k]-=rhl;
    39     a[j+k+i]=x-y; if (a[j+k+i]<0) a[j+k+i]+=rhl;
    40       }
    41   }
    42   if (f==1) return; reverse(a+1,a+n); RG int inv=qpow(n,rhl-2);
    43   for (RG int i=0;i<n;++i) a[i]=1LL*a[i]*inv%rhl;
    44   return;
    45 }
    46 
    47 int main(){
    48 #ifndef ONLINE_JUDGE
    49   freopen("robot.in","r",stdin);
    50   freopen("robot.out","w",stdout);
    51 #endif
    52   cin>>n; pre();
    53   for (RG int i=1;i<=n;++i)
    54     c[i]=1LL*fac[i<<1]*ifac[i]%rhl*ifac[i]%rhl*inv[i+1]%rhl;
    55   for (m=1;m<=(n<<1);m<<=1) ++lg; a[0]=1;
    56   for (RG int i=1;i<=n;++i) if (!(i&1)) a[i]=1LL*c[i>>1]*ifac[i]%rhl;
    57   for (RG int i=0;i<m;++i) rev[i]=rev[i>>1]>>1|((i&1)<<(lg-1));
    58   NTT(a,m,1); for (RG int i=0;i<m;++i) a[i]=1LL*a[i]*a[i]%rhl;
    59   NTT(a,m,-1); for (RG int i=0;i<=n;++i) a[i]=1LL*a[i]*fac[i]%rhl;
    60   for (RG int i=0;i<=n;++i)
    61     ans=(ans+1LL*a[i]*fac[n]%rhl*ifac[i]%rhl*ifac[n-i])%rhl;
    62   printf("%d
    ",ans); return 0;
    63 }
  • 相关阅读:
    日志分析zz
    通过一个非法的指针或者NULL指针调用成员函数会发生什么?
    Android应用程序进程启动过程的源代码分析
    Android应用程序消息处理机制(Looper、Handler)分析
    Android应用程序绑定服务(bindService)的过程源代码分析
    Android系统进程Zygote启动过程的源代码分析
    Android应用程序注册广播接收器(registerReceiver)的过程分析
    Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
    Android系统默认Home应用程序(Launcher)的启动过程源代码分析
    Android应用程序安装过程源代码分析
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7413919.html
Copyright © 2020-2023  润新知