ABC372F

赛后 20 分钟做出来这道题,糖丸了。

针对这道题,可以轻松设计出 dpi,jdp_{i,j}dpi,j 表示走完了第 iii 步,目前在 jjj 位置的方案总数。则一开始有 dpi,j=dpi−1,prejdp_{i,j}=dp_{i-1,pre_j}dpi,j=dpi1,prej,其中 prejpre_jprej 表示在前 nnn 条边中,指向 jjj 点的那个点。然后枚举后 mmm 条边 (ui,vi)(u_i,v_i)(ui,vi),则有 dpi,vi=dpi,vi+dpi−1,uidp_{i,v_i}=dp_{i,v_i}+dp_{i-1,u_i}dpi,vi=dpi,vi+dpi1,ui

可以轻松的写出如下代码:

#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int dp[2010][2010];//这里2010是一个乱写的数,懒得用滚动数组优化了。
int a[55][2];
const int mod = 998244353;

int main() {
	cin >> n >> m >> k;
	dp[0][1] = 1;
	for (int i = 1; i <= m; i++)
		cin >> a[i][0] >> a[i][1];
	for (int i = 1; i <= k; i++) {
		for (int j = 1; j <= n; j++)
			dp[i][j] = dp[i - 1][(j == 1 ? n : j - 1)];//这是第一部分
		for (int j = 1; j <= m; j++) {
			int u = a[j][0], v = a[j][1];
			dp[i][v] = (dp[i][v] + dp[i - 1][u]) % mod;//这是第二部分
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans = (ans + dp[k][i]) % mod;
	cout << ans;
	return 0;
}

这样的时间复杂度是 O((n+m)k)O((n+m)k)O((n+m)k) 的,很明显需要更加深入的优化。

观察到第 111 部分可以优化掉,因为这就相当于一个 dpi−1dp_{i-1}dpi1 的循环右移过程。但是要处理这个过程则需要使用很多的数学计算。

如果是循环右移的话就好办了,这一步实际上不需要动原来的数组,只需要记录一个右移的偏移量 ypypyp 即可(不要在意我这个名称),对于每一个下标 jjj,只需要 dpi,j=dpi−1,(j+yp+n−1) mod n+1dp_{i,j}=dp_{i-1,(j + yp + n - 1)\bmod n + 1}dpi,j=dpi1,(j+yp+n1)modn+1 就可以完成转移。这就是第一部分的表示。(当然,并不需要真正的执行,我们可以使用 (j+yp+n−1) mod n+1(j + yp + n - 1)\bmod n + 1(j+yp+n1)modn+1 代表 jjj 进行转移即可)

至于如何第二部分的表示,由于最后要使用滚动数组,根据一番推算可以发现 dpi,(v+yp+n−1) mod n+1=dpi,(v+yp+n−1) mod n+1+dpi−1,(u+yp) mod n+1dp_{i,(v + yp + n - 1)\bmod n + 1} = dp_{i,(v + yp + n - 1)\bmod n + 1} + dp_{i-1,(u + yp) \bmod n + 1}dpi,(v+yp+n1)modn+1=dpi,(v+yp+n1)modn+1+dpi1,(u+yp)modn+1,这样就可以表示了。

由于最后还需要搞一个滚动数组,所以还需要 lstlstlst 数组,就可以把状态优化掉一维。

具体看代码。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010;
int a[55][2];
int n, m, k;
int py;
int lst[N], dp[N];
const int mod = 998244353;

signed main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= m; i++)
		cin >> a[i][0] >> a[i][1];
	lst[1] = 1, dp[1] = 1;
	for (int i = 1; i <= k; i++) {
		py++;//这里我是根据左偏移量算出右偏移量
		py %= n;
		int yp = n - py;//右偏移量
		for (int j = 1; j <= m; j++) {
			int u = a[j][0], v = a[j][1];
			dp[(v + yp + n - 1) % n + 1] += lst[(u + yp) % n + 1];
			dp[(v + yp + n - 1) % n + 1] %= mod;//转移
		}
		for (int j = 1; j <= m; j++) {
			int u = a[j][0], v = a[j][1];
			lst[(v + yp + n - 1) % n + 1] = dp[(v + yp + n - 1) % n + 1];
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans = (ans + dp[i]) % mod;
	cout << ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值