CF1152 F. Neko Rules the Catniverse (dp)

題意

一條長爲 \(n\) 的數軸,可以從任意整點 \(\in [1, n]\) 出發,假設當前在 \(x\) ,下一步能到達的點 \(y\) 需要滿足,\(y\) 從未到過,且 \(1 \le y \le x + m\) ,問長恰好爲 \(k\) 的合法路徑條數。

數據範圍

對於 \(F1\)\(1 \le n \le 10^5, 1 \le k \le \min(n, 12), 1 \le m \le 4\)

對於 \(F2​\)\(1 \le n \le 10^9​\)

題解

比較 \(\text{tricky}\) 的一個題。

考慮我們當前假設經過的路徑爲 \(v_1, v_2, \cdots, v_p\) ,我們當前可以添加一個 \(x < \min_{i = 1}^{p} \{v_i\}\) 。顯然我們是一定可以添加到隊尾的,其次我們可以添加到那些 \(v_i \le x + m\) 的前面。

那麼我們就得到一個很顯然的 \(dp\) 了,考慮從大到小依次考慮每個數填還是不填就能輕鬆轉移了。

具體來說設 \(dp[i][j][sta]\) 爲當前在第 \(i\) 個位置,路徑長度爲 \(j\) ,最後 \(m\) 個位置狀壓後的狀態爲 \(sta\)

每次轉移的時候,如果不填直接轉過去,填的話可以轉到後 \(m\) 個有 \(1\) 的狀態以及隊尾,也就是 \(1 + bitcount(sta)\)

這樣 \(dp\) 剛好每條路都能一一對應上。

對於 \(F1\) 直接 \(\mathcal O(nk2^m)\) 就行了,\(F2\) 考慮利用矩陣快速冪優化到 \(\mathcal O((k2^m)^3 \log n)\) 。(說實話 \(F2\) 沒啥意思。。)

代碼

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("F1.in", "r", stdin);
    freopen ("F1.out", "w", stdout);
#endif
}

const int N = 1e5 + 1e3, K = 14, M = 4, Mod = 1e9 + 7;

int dp[N][K][1 << M];

int main() {

    File();

    int n = read(), k = read(), m = read();

    dp[0][0][0] = 1;
    Rep (i, n) For (j, 0, k) Rep (sta, 1 << m) {
        (dp[i + 1][j][sta >> 1] += dp[i][j][sta])%= Mod;
        int res = dp[i][j][sta] * (1ll + __builtin_popcount(sta)) % Mod;
        (dp[i + 1][j + 1][(sta >> 1) | (1 << m - 1)] += res) %= Mod;
    }

    int ans = 0;
    Rep (sta, 1 << m)
        (ans += dp[n][k][sta]) %= Mod;
    printf ("%d\n", ans);

    return 0;

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章