[Codeforces 1716D] Chip Move

https://codeforces.com/contest/1716/problem/D

题意:
从点 \(0\) 出发,第 \(i\) 步只能走 \(k + i - 1\) 的倍数步长,问到达 \(1\) ~ \(n\) 的方案数。

思路:
因为每次 \(k\) 都会 \(+1\) ,所以最坏情况下走的步长和为一个等差数列求和,因此我们至多只会走 \(\sqrt{n}\) 步。有个很显然的 \(dp\) 做法,\(dp[i][j]\) 代表到点 \(i\) 时,用了 \(j\) 步的方案数,但是数组太大了开不下,并且该状态向后面转移时需要枚举所有能到的点,时空复杂度都不够优秀。

为了避免枚举后续能到的点,我们考虑填表法。我们枚举步长 \(i\) ,当前点 \(j\) ,前面合法的点为上一次能到的点 \(x\) ,并且 \(x \equiv j \pmod{i}\)。那么一边走一边把这些点的贡献加入一个桶就行。时间复杂度 \(O(n\sqrt{n})\)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;

void add(int &a, int b) {
    a += b;
    if (a >= mod) a -= mod;
}

int dp[200005], pre[200005], ans[200005];
void solve() {
    int n, k;
    cin >> n >> k;
    pre[0] = 1;
    for (int i = k, sum = k; sum <= n; sum += ++i) {
        for (int j = sum - i; j <= n; ++j) {
            if (j < sum) {
                add(pre[j % i], dp[j]);
                continue;
            }
            int tmp = dp[j];
            dp[j] = pre[j % i];
            add(pre[j % i], tmp);
            add(ans[j], dp[j]);
        }
        for (int j = 0; j <= i; ++j) pre[j] = 0;
    }
    for (int i = 1; i <= n; ++i) {
        cout << ans[i] << ' ';
    }
}

int main() {
#ifndef stff577
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    cout << fixed << setprecision(20);
#endif
    int t = 1;
    while (t--) solve();
    return 0;
}
posted @ 2022-08-12 19:50  stff577  阅读(86)  评论(0编辑  收藏  举报