Official

E - Safety Journey Editorial by mechanicalpenciI


動的計画法でこの問題を解くことを考えます。
\(dp[i][j]=(A_0=0\) かつ \(A_i=j\) であるような都市の列 \((A_0, \ldots A_i)\) であって、各 \(0\leq i'\leq i-1\) について、 \(A_{i'}\)\(A_{i'+1}\) が道でつながっているようなものの数)とします。 すると、\(dp[0][0]=1\) , \(dp[0][1]=\cdots=dp[0][N]=0\) であり、 \(dp[i+1][j]\)\(S_j=\{j'|都市j と都市j 'は道でつながっている\}\)を用いて、

\[dp[i+1][j]=\displaystyle\sum_{j'\in S_j}dp[i][j']\]

と書く事が出来ます。

しかしこれをそのまま計算すると、\(O(N^2K)\) の計算量がかかり、時間制限に引っかかってしまいます。そこで、 これを\(S'_j=\{j'|都市j と都市j 'は道でつながっていない\}\)を用いて、

\[dp[i+1][j]=\displaystyle\sum_{j'=1}^Ndp[i][j']-\displaystyle\sum_{j'\in S_j}dp[i][j']\]

と書きかえてみます。 第 \(1\) 項は \(j\) によらないため、各 \(i\) ごとに一度計算すればよく、計算には \(1\) 回当たり \(O(N)\) がかかります。 第 \(2\) 項は \(S'_j\) の要素数がすべての \(j\) についてあわせて \(2M+N\) 個しか無い事からこちらも各 \(i\) について \(O(N+M)\) で計算できます。\((j\) 自身が含まれることに注意) よってこの場合は \(i\to i+1\) の遷移が\(O(N+M)\) で行えるため、全体で \(O((N+M)K)\) であり、求めたいものは \(dp[K][1]\) であるため、これでこの問題を解く事が出来ました。

C++による実装例:

#include <bits/stdc++.h>
using namespace std;

#define N 6000
#define MOD 998244353
#define ll long long
#define rep(i, n) for(int i = 0; i < n; ++i)

int main(void) {
	int n, m, k, u, v, sz;
	vector<int>e[N];
	ll x, y, z, s;
	ll dp[N];
	ll dp2[N];

	cin >> n >> m >> k;
	rep(i, m) {
		cin >> u >> v;
		e[u - 1].push_back(v - 1);
		e[v - 1].push_back(u - 1);
	}

	rep(i, n)dp[i] = (ll)0;
	dp[0] = (ll)1;

	rep(i, k) {
		s = (ll)0;
		rep(j, n)s += dp[j];
		rep(j, n) {
			dp2[j] = s - dp[j];
			sz = e[j].size();
			rep(ii, sz)dp2[j] -= dp[e[j][ii]];
			dp2[j] %= MOD;
		}
		rep(j, n)dp[j] = dp2[j];
	}

	cout << dp[0] << endl;

	return 0;
}

posted:
last update: