CF EDU 133 D - Chip Move

前缀和优化DP

Problem - D - Codeforces

题意

给定 n,k (1<= n, k <= 2e5), 若当前位置是 i,已经走了 j 次,则下一次只能走 j + k - 1 的倍数步;

求从 0 开始,走到 [1, n] 中每个位置的方案数

思路

先想一个朴素dp

\(f[i][j]\) 为从 0 开始,用了 j 步走到 i 的方案数

最后一步是走了 d = k + j - 1 步, 枚举最后一步的可能情况,即 \(f[i][j]=f[i-d][j-1]+f[i-2*d][j-1]+...\)

因为 \(j\)\(sqrt(i)\) 级别的;

根据调和级数,总转移是 \(n*logn*\sqrt n\) ,因此总时间复杂度为 \(O(n*logn*\sqrt n)\)

状态数是 \(n*\sqrt n\), 空间复杂度是 \(O(n\sqrt n)\)

考虑优化时空复杂度

空间

显然可以用滚动数组优化掉 \(j\) 那一维,若当前枚举到第 j 步,用 \(f[i]\) 表示 j 步走到 i 的方案数,\(tmp[i]\) 表示 j - 1 步走到 i 的方案数

时间

\(f[i]=tmp[i-d]+tmp[i-2*d]+tmp[i-3*d]+tmp[i-4*d]+...\)

用前缀和优化转移即可

也可以用 \(f[i]=tmp[i-d]+f[i-d]\)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2e5 + 10;
const int mod = 998244353;
int n, k;
int f[N], ans[N], tmp[N];

int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> k;
	f[0] = 1;
	for (int j = 1; j < sqrt(2 * n) + 2; j++)
	{
		memset(tmp, 0, sizeof tmp);
		for (int i = 1; i <= n; i++)
		{
			int now = j + k - 1;
			if (i - now >= 0)
				tmp[i] = (tmp[i-now] + f[i-now]) % mod;
			ans[i] = (ans[i] + tmp[i]) % mod;
		}
		memcpy(f, tmp, sizeof f);
	}
	for (int i = 1; i <= n; i++)
		cout << ans[i] << " ";
	cout << endl;

    return 0;
}

PS

我自己想的dp是先枚举 i,再从 \([1,\sqrt{2*n}]\) 枚举 j

\(f[i][j]=f[i+1-k-j][j-1]+f[i+1-k-j][j]\)

第 j 次跳的步数是 d = k + j - 1

意思是

  1. i 是第 j 次跳了 1 个 d 步过来的
  2. i 是跳了 t 次 d 步来的,但是这个方案数可以由用 j 次到位置 i - d 的方案数继承而来(如果第 j 次能到 i - d,那多跳一个 d 的方案数是一样的)

这样的时空复杂度都是 \(O(n*\sqrt n)\)

f[0][0] = 1;
for (int i = 1; i <= n; i++)
    f[i].resize(sqrt(2*i) + 1);
for (int i = 1; i <= n; i++)
{
    for (int j = 1; j < f[i].size(); j++)
    {
        if (i + 1 - k - j < 0)
            break;
        if (f[i+1-k-j].size() <= j - 1)
            break;
        add(f[i][j], f[i+1-k-j][j-1]);
        if (f[i+1-k-j].size() > j)
            add(f[i][j], f[i+1-k-j][j]);
    }
}

但不太能用滚动数组优化空间,就寄了。。。

而且常数略大可能跑不出来,本地跑 n = 2e5, k = 1 用 4s,正解只用 800ms

posted @ 2022-08-12 19:46  hzy0227  阅读(35)  评论(0编辑  收藏  举报