LG P3990 【SHOI2013】 超级跳马 解题报告

LG P3990 【SHOI2013】 超级跳马 解题报告

地址

看到 n ≤ 50 , m ≤ 1 0 9 n\leq 50,m\leq 10^9 n50,m109 的数据范围,可以想到矩阵快速幂。不论如何,我们先从 dp 推起。

f ( i , j ) f(i,j) f(i,j) 表示到 ( i , j ) (i,j) (i,j) 的方案数,则

f ( i , j ) = ∑ k = j − 1 − 2 t ≥ 1 , t ∈ N f ( { i − 1 , i , i + 1 } , k ) f(i,j)=\sum_{k=j-1-2t \ge 1,t\in \mathrm N}f(\{i-1,i,i+1\},k) f(i,j)=k=j12t1,tNf({i1,i,i+1},k)

(其中 j ≥ 2 j\ge 2 j2

f ( i , j − 2 ) = ∑ k = j − 3 − 2 t ≥ 1 , t ∈ N f ( { i − 1 , i , i + 1 } , k ) f(i,j-2)=\sum_{k=j-3-2t\ge 1,t\in \mathrm N}f(\{i-1,i,i+1\},k) f(i,j2)=k=j32t1,tNf({i1,i,i+1},k)

(其中 j ≥ 4 j\ge 4 j4

综合以上两式可得

f ( i , j ) = f ( { i − 1 , i , i + 1 } , j − 1 ) + f ( i , j − 2 ) f(i,j)=f(\{i-1,i,i+1\}, j-1)+f(i,j-2) f(i,j)=f({i1,i,i+1},j1)+f(i,j2)

(其中 j ≥ 4 j\ge 4 j4

注意这些 j j j 的范围很重要。

这个递推显然是 O ( n m ) O(nm) O(nm) 的,但是我们发现这是一个线性齐次递推,所以可以矩阵处理:

[ f ( 1 , j ) f ( 2 , j ) ⋯ f ( n , j ) f ( 1 , j − 1 ) f ( 2 , j − 1 ) ⋯ f ( n , j − 1 ) ] = A [ f ( 1 , j − 1 ) f ( 2 , j − 1 ) ⋯ f ( n , j − 1 ) f ( 1 , j − 2 ) f ( 2 , j − 2 ) ⋯ f ( n , j − 2 ) ] = A j − 3 [ f ( 1 , 3 ) f ( 2 , 3 ) ⋯ f ( n , 3 ) f ( 1 , 2 ) f ( 2 , 2 ) ⋯ f ( n , 2 ) ] \begin{bmatrix} f(1,j) \\ f(2,j) \\ \cdots \\ f(n,j) \\ f(1,j-1) \\ f(2,j-1) \\ \cdots \\ f(n,j-1) \end{bmatrix}= A \begin{bmatrix} f(1,j-1) \\ f(2,j-1) \\ \cdots \\ f(n,j-1) \\ f(1,j-2) \\ f(2,j-2) \\ \cdots \\ f(n,j-2) \end{bmatrix}= A^{j-3} \begin{bmatrix} f(1,3) \\ f(2,3) \\ \cdots \\ f(n,3) \\ f(1,2) \\ f(2,2) \\ \cdots \\ f(n,2) \end{bmatrix} f(1,j)f(2,j)f(n,j)f(1,j1)f(2,j1)f(n,j1) =A f(1,j1)f(2,j1)f(n,j1)f(1,j2)f(2,j2)f(n,j2) =Aj3 f(1,3)f(2,3)f(n,3)f(1,2)f(2,2)f(n,2)

推到 3 是因为 j ≥ 4 j\ge 4 j4 时才有上面的那个是式子成立。 A A A 是容易构造的。

本题注意特判:根据 n n n 的不同,初值列向量是不同的; m m m 需要特判 m < 3 m<3 m<3

时间复杂度 O ( n 3 log ⁡ m ) O(n^3\log m) O(n3logm)

/*
 * @Author: rijuyuezhu
 * @Date: 2022-07-30 08:05:37
 * @Description: https://www.luogu.com.cn/problem/P3990
 * @Tag: 数学,矩阵,快速幂,DP
*/
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), tt == ss) ? EOF : *ss++)
ll read() {
   ll x = 0, f = 1; char ch = getchar();
   for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
   for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
   return x * f;
}

const int MAXN = 105;
const int P = 30011;
struct mint {
   int v;
   mint(int v = 0) : v(v) {}
};
int MOD(int v) {return v >= P ? v - P : v;}
mint operator + (mint a, mint b) {return MOD(a.v + b.v);}
mint operator - (mint a, mint b) {return MOD(a.v - b.v + P);}
mint operator * (mint a, mint b) {return a.v * b.v % P;}
int Mat_size;
struct Mat {
   mint A[MAXN][MAXN];
   mint* operator [] (int k) {return A[k];}
   const mint* operator [] (int k)const {return A[k];}
   void clear() {
      for(int i = 1; i <= Mat_size; i++)
         for(int j = 1; j <= Mat_size; j++)
            A[i][j] = 0;
   }
   void unit() {
      for(int i = 1; i <= Mat_size; i++)
         for(int j = 1; j <= Mat_size; j++)
            A[i][j] = (i == j);
   }
};
Mat operator * (const Mat& a, const Mat& b) {
   Mat c; c.clear();
   for(int i = 1; i <= Mat_size; i++)
      for(int k = 1; k <= Mat_size; k++)
         for(int j = 1; j <= Mat_size; j++)
            c[i][j] = c[i][j] + a[i][k] * b[k][j];
   return c;
}
Mat qpow(Mat a, int n) {
   Mat ret; ret.unit();
   for(; n; n >>= 1, a = a * a)
      if(n & 1) ret = ret * a;
   return ret;
}

int n, m;
Mat B, ans;
int main() {
   n = read(), m = read();
   if(m == 1) {
      printf("%d\n", n == 1 ? 1 : 0);
   } else if(m == 2) {
      printf("%d\n", n == 1 || n == 2 ? 1 : 0);
   } else {
      Mat_size = 2 * n; B.clear();
      for(int i = 1; i <= n; i++) {
         B[i][i] = B[i+n][i] = B[i][i+n] = 1;
         if(i > 1) B[i][i-1] = 1;
         if(i < n) B[i][i+1] = 1;
      }
      ans = qpow(B, m-3);
      if(n == 1) printf("%d\n", (ans[n][1] + ans[n][n+1]).v);
      else if(n == 2) printf("%d\n", (2 * ans[n][1] + 2 * ans[n][2] + ans[n][n+1] + ans[n][n+1]).v);
      else printf("%d\n", (2 * ans[n][1] + 2 * ans[n][2] + ans[n][3] + ans[n][n+1] + ans[n][n+2]).v);
   }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值