zoukankan      html  css  js  c++  java
  • bzoj4417[SHOI2013]超级跳马

    还有不到一星期就是HEOI2017了,bzoj怎么还是有人卡评测啊,不爽...以及写题解可以涨RP?为了省选攒RP中....

    题意

    现有一个n行m列的棋盘,一只马欲从棋盘的左上角跳到右下角.每一步它向右跳奇数列,且跳到本行或相邻行.跳跃期间,马不能离开棋盘.n<=50,m<=10^9

    分析

    一看这个题面和题目描述,这题就很像一个矩阵快速幂.然后可耻地想偏了...一开始想分开算行的方案数和列的方案数然后乘起来,然后发现这样需要分别统计跳1,3,5,7步(或者2,4,6,8...步,视m的奇偶性)到终点的方案数,因为走5步到达第n行和走7步到达第n行的方案数必然是不一样的.然后发现这样是构建不出矩阵的.
    然后我推了一下不考虑走的步数,只要求到达第m列的方案数,发现是Fibonacci数.

    为什么是Fibonacci?
    记f[m]为走到第m列的方案数,考虑最后走到第m列的一步.
    如果这一步大小为1,那么之前走到了第m-1列,因此给f[m]加上f[m-1].
    如果这一步大小不为1,那么把最后一步的大小减2,可以得到走到第m-2列的一种方案,因此给f[m]加上f[m-2].
    于是f[m]=f[m-1]+f[m-2]

    然后傻叉liu_runda沿着一开始的思路,有一瞬间竟然想用走到第m列的方案数乘上走到第n行的方案数得到最终答案...这肯定是不行的,因为这些在列上走到第m列的方案走的步数是不知道的,因此走到第n行的方案数是不同的,没法算出来直接乘.

    拆成两部分算方案数再乘起来的思路行不通...那么我们尝试直接计算.刚刚推的fibonacci数的结论虽然并没有什么用,但推导的过程给了我们一个启示:走到第m列的方案数只与第m-1列和第m-2列有关.一个2*50的状态数是比较少的.我们可以考虑直接定义状态f[i][j]表示走到第i列第j行的方案数,利用矩阵乘法进行转移,矩阵里保存连续两列的状态即可.
    然而这么直接乘还是会有问题.
    (捂脸熊.jpg)
    (x,y)表示第x列第y行,一开始,有一种方案是位于(1,1).如果按照刚才建立的矩阵直接乘,我们会认为这个位于(1,1)的方案可以变成位于(3,1)的方案,因为我们假定马肯定走了一步才到达(1,1),但实际上并没有这么走一步,然后就多算了.可以发现我们把走到(3,1)的方案数多算了一个,最终答案中多算的是"假设我们走了一步到达(3,1),从(3,1)走到(m,n)的方案数",这个也可以快速幂解决.而这个快速幂就不需要考虑"从(3,1)转移到(5,1)是否会重复",因为我们要算的只是(3,1)位置多算的一个1对最终(m,n)答案的贡献,按照和一开始相同的方式计算即可(一开始重复计算的那个方案也假设我们走了一步到达(3,1)).

    #include<cstdio>
    #include<cstring>
    const int mod=30011;
    int sz;
    struct matrix{
      int a[105][105];
      matrix(){
        memset(a,0,sizeof(a));
      }
      matrix(int x){
        memset(a,0,sizeof(a));
        for(int i=0;i<55;++i)a[i][i]=x;
      }
      matrix operator *(const matrix &B)const{
        matrix C;
        for(int i=1;i<=sz;++i){
          for(int j=1;j<=sz;++j){
    	    for(int k=1;k<=sz;++k){
    	      C.a[i][k]=(C.a[i][k]+a[i][j]*1ll*B.a[j][k]%mod)%mod;
    	    }
          }
        }
        return C;
      }
    }A,B,C;
    matrix qpow(matrix A,int x){
      matrix ans(1);
      for(;x;x>>=1,A=A*A){
        if(x&1)ans=ans*A;
      }
      return ans;
    }
    int main(){
      int n,m;scanf("%d%d",&n,&m);
      sz=2*n;
      for(int i=1;i<=n;++i){
        A.a[n+i][i]=1;A.a[i][n+i]=1;A.a[n+i][n+i]=1;
      }
      for(int i=1;i<n;++i){
        A.a[n+i][n+i+1]=1;
      }
      for(int i=2;i<=n;++i){
        A.a[n+i][n+i-1]=1;
      }
      B=qpow(A,m-1);
      int ans=B.a[n+1][n+n];
      if(m>=3){
        C=qpow(A,m-3);
        ans=(ans-C.a[n+1][n+n]+mod)%mod;
      }
      printf("%d
    ",ans);
      return 0;
    }
    
    
  • 相关阅读:
    Log4net源码分析(一)
    ASP.NET MVC 框架是 .NET 平台 Web 开发的主流,但是并不适合初学者
    详解ASP.NET MVC应用程序请求生命周期
    ELMAH (ASP.NET错误日志处理)使用方法
    ElMAH(ASP.NET错误日志记录与通知)系列文章基础应用篇
    Log4net源码分析(三)
    Log4net 框架系列:log4net日志文件在asp.net中的应用实例记录系统错误
    ELMAH(ASP.NET错误日志记录与通知)系列文章概念篇
    Log4net源码分析(二)
    在VS2010中配制Elmah邮件发送到Gmail
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6720198.html
Copyright © 2011-2022 走看看