[bzoj4417] [Shoi2013] 超级跳马

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4417

我们不难发现,这是一道动归题。

考虑最原始的动归:f[i][j]表示从起点走到(i,j)这个点的方案数。

不难推出f[i][j]=Σ(f[i][j-2k+1]+f[i-1][j-2k+1]+f[i+1][j-2k+1]) 其中k∈[1,floor(j/2)]。floor(x)表示将x向上取整。

显然,这个式子的复杂度为O(n*m^2),愉悦地TLE~。

再研究些许发现其实可以用前缀和优化该式子,复杂度被削掉一个m,变为O(n*m)。但显然,这个还是会超时的,得继续优化。

题目中,n的范围只有50,如果真的是一道单纯的暴力题,n的范围不可能这么小。我们考虑用矩阵快速幂优化该式子。记录答案的矩阵为1*2n的矩阵,该矩阵左侧n个位置存储f[i],右侧n个位置存储f[i-1]。 我们构造一个2n*2n的转移矩阵,转移矩阵需满足记录答案的矩阵乘上转移矩阵后变成左侧n个位置存储f[i+1],右侧n个位置存储f[i]。

大致的样子如图:以n=5的矩阵举例(n为其他的和这个长的差不多),中间十字交叉线是帮助大家理解的,矩阵是10*10的!!

 

构造矩阵时需特别注意n=1的情况!!(我就被这个坑了)

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define M 110
 5 #define MOD 30011
 6 using namespace std;
 7 struct matrix{
 8     int a[M][M],n,m;
 9     matrix(){memset(a,0,sizeof(a)); n=m=0;}
10     matrix(int nn,int mm){memset(a,0,sizeof(a)); n=nn; m=mm;}
11     friend matrix operator *(matrix a,matrix b){
12         matrix c=matrix(a.n,b.m);
13         for(int i=1;i<=a.n;i++)
14         for(int j=1;j<=b.m;j++)
15         for(int k=1;k<=a.m;k++)
16         c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%MOD;
17         return c;
18     }
19     friend matrix operator ^(matrix a,int p){
20         matrix c=a; p--;
21         if(p<0) {memset(c.a,0,sizeof(c.a)); if(p==-1) c.a[1][1]=1; return c;}
22         while(p){
23             if(p&1) c=c*a;
24             p>>=1; a=a*a;
25         } 
26         return c;
27     }
28 }f,f1,f2,ans1,ans2;
29 int n,m;
30 int main(){
31     scanf("%d%d",&n,&m);
32     f=matrix(n<<1,n<<1); ans1=ans2=matrix(1,n<<1);
33     ans1.a[1][1]=ans2.a[1][1]=1;
34     for(int i=1;i<=n;i++) f.a[i][i-1]=f.a[i][i]=f.a[i][i+1]=1;
35     f.a[1][0]=f.a[n][n+1]=0;
36     for(int i=1;i<=n;i++) f.a[i][n+i]=f.a[n+i][i]=1;
37     f1=f^(m-1); f2=f^(m-3);
38     ans1=ans1*f1; ans2=ans2*f2;
39     printf("%d\n",(ans1.a[1][n]-ans2.a[1][n]+MOD)%MOD);
40 }

 

posted @ 2017-10-26 10:40  AlphaInf  阅读(453)  评论(0编辑  收藏  举报