题目链接
AtCoder Beginner Contest 372 F
思路
对于这种计数问题,我们很容易想到dp。
我们假设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i次移动,到第 j j j号节点的方案数,则状态转移方程为:
d p [ v ] [ j ] = d p [ v − 1 ] [ j − 1 ] + d p [ u ] [ j − 1 ] dp[v][j] = dp[v-1][j-1] + dp[u][j-1] dp[v][j]=dp[v−1][j−1]+dp[u][j−1]
显然,无论是时间复杂度还是空间复杂度都无法接受。
我们发现, m m m最大不会超过50,因此我们可以考虑以 m m m为突破口对dp进行优化。
我们发现,状态转移方程的前半部分实际上是对 d p dp dp数组进行循环移动。
因为 d p [ v − 1 ] [ j − 1 ] dp[v-1][j-1] dp[v−1][j−1]实际上就是 d p dp dp数组循环移动一位,且 m = 0 m=0 m=0时答案为 1 1 1,因此我们可以只考虑 m m m。
我们可以转换一下,我们假设 d p dp dp数组不动,对 m m m条边进行移动,每次 m m m条边移动后的端点进行更新答案,相当于变相的移动了 d p dp dp数组。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5;
const int mod = 998244353;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m, k;
int dp[N], pre[N];
vector<pair<int, int>> edge;
int MOD(int x)
{
return (x % n + n) % n;
}
void solve()
{
cin >> n >> m >> k;
for (int i = 1, u, v; i <= m; i++)
{
cin >> u >> v;
u--, v--;
edge.push_back({u, v});
}
dp[0] = 1;
for (int i = 1; i <= k; i++)
{
for (auto [u, v] : edge)
{
u = MOD(u - i + 1);//上一层的u
pre[u] = dp[u];
}
for (auto [u, v] : edge)
{
dp[MOD(v - i)] = dp[MOD(v - i)] + pre[MOD(u - i + 1)];
dp[MOD(v - i)] %= mod;
}
}
int ans = 0;
for (int i = 0; i < n; i++)
{
ans = (ans + dp[i]) % mod;
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}