Luogu P10812

题目描述

给定一根 \(1\)\(N\) 的数轴。一开始有一个棋子在 \(N\)。每次棋子 \(x\) 可以跳到 \(x-1,x+1\)\(x\) 的因子处(不能超出 \(1\)\(N\))。

每个点只能到达一次。求棋子到达 \(1\) 的方案数。

思路

由于求倍数比因子简单,所以把问题变成从 \(1\)\(N\),每次跳倍数。

我们可以发现,棋子的行走路径由两种类型的路拼在一起:

image

由于有先跳倍数再 \(-1\) 的跳法,此时跳的倍数必须大于走过的最远的位置,所以状态中要记录最远走到哪里。

\(dp_{i,j}\) 表示当前在 \(i\),最远走到了 \(j\) 的方案数。

\(i=j\) 时,我们有转移 \(dp_{i+1,i+1}\leftarrow dp_{i,j}\)

当然我们也可以跳倍数,也就是对于每个 \(k=i\cdot m(m>1)\),那么都有转移 \(dp_{x,k}\leftarrow dp_{i,j}(j<x\le k)\)

而这种转移可以使用前缀和维护。

空间复杂度 \(O(N^2)\),时间复杂度 \(O(N^2\log N)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 5005;

int n, MOD, dp[MAXN][MAXN], sum[MAXN][MAXN];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> MOD;
  dp[1][1] = 1;
  for(int i = 1; i <= n; ++i) {
    for(int j = i; j <= n; ++j) {
      sum[j][i] = sum[j][i] + sum[j][i - 1] - (sum[j][i] + sum[j][i - 1] >= MOD ? MOD : 0);
      //cout << sum[j][i] << " \n"[j == n];
      dp[i][j] = dp[i][j] + sum[j][i] - (dp[i][j] + sum[j][i] >= MOD ? MOD : 0);
      //cout << dp[i][j] << " \n"[j == n];
      if(i == j) {
        dp[i + 1][max(i + 1, j)] = dp[i + 1][max(i + 1, j)] + dp[i][j] - (dp[i + 1][max(i + 1, j)] + dp[i][j] >= MOD ? MOD : 0);
      }
      for(int k = 2 * i; k <= n; k += i) {
        if(k > j) {
          sum[k][j + 1] = sum[k][j + 1] + dp[i][j] - (sum[k][j + 1] + dp[i][j] >= MOD ? MOD : 0);
        }
      }
    }
  }
  cout << dp[n][n];
  return 0;
}
posted @ 2024-09-17 16:34  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报