Luogu P10812

Yaosicheng124發表於2024-09-17

題目描述

給定一根 \(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;
}

相關文章