King Bombee(圖上dp)
阿新 • • 發佈:2022-03-27
題意
給定一個\(n\)個點\(m\)邊的無向簡單圖。現在給你\(4\)個整數\(S\),\(T\),\(K\),\(X\)。請你求出有多少條從\(S\)到\(T\)的路徑滿足如下條件:
- 途中經過\(K\)條邊
- 經過點\(X\)的次數為偶數(可能為\(0\))
資料範圍
\(2 \leq n \leq 2000\)
\(1 \leq m \leq 2000\)
\(1 \leq K \leq 2000\)
思路
首先不管第二個條件,只考慮第一個條件。我們考慮使用DP。令\(f(i,j)\)表示起點是\(S\),終點是\(j\),經過\(i\)條邊的路徑總數,則:
-
\(f(0, S) = 1\),\(f(0, j) = 0(j \neq S)\)
- \(f(i, j) = \sum_\limits{k \in adj(j)} f(i - 1, k)\)
下面說明一下,為什麼這裡可以使用DP,而不用考慮後效性問題。
我們可以將圖拓展一下,把原先的圖複製\(K\)份。然後對這\(K + 1\)個圖從\(0\)開始標號,我們將這個標號稱為“層”。
轉移陣列\(f(i, j)\)表示的是從第\(0\)層的\(S\)點到第\(i\)層的\(j\)點的方案數。在進行轉移的時候,到達當前層的方案數只能從上一層進行轉移,不會從同一層的點轉移(簡單圖)。
因此,在這裡使用DP不存在後效性問題。
現在,把第二個條件考慮在內,在DP中說到奇偶問題,一個常用的技巧就是將奇數看作是“\(1\)
令\(f(i, j, 0/1)\)表示起點是\(S\),終點是\(j\),經過\(i\)條邊且經過\(X\)點次數為偶數/奇數的路徑總數,則:
- \(f(0, S, 0) = 1\)
- \(f(i, j, t) = \sum_\limits{k \in adj(j)} f(i - 1, k, t)(j \ne X)\)
- \(f(i, X, t) = \sum_\limits{k \in adj(X)} f(i - 1, k, 1 - t)\)
最終答案即為:\(f(K, T, 0)\)
程式碼
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 2010, M = 2 * N, mod = 998244353; int n, m, K; int S, T, X; int h[N], e[M], ne[M], idx; ll f[N][N][5]; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } int main() { scanf("%d%d%d%d%d%d", &n, &m, &K, &S, &T, &X); memset(h, -1, sizeof h); for(int i = 0; i < m; i ++) { int a, b; scanf("%d%d", &a, &b); add(a, b), add(b, a); } f[0][S][0] = 1; for(int i = 1; i <= K; i ++) { for(int j = 1; j <= n; j ++) { for(int k = 0; k < 2; k ++) { if(j == X) { for(int l = h[j]; ~l; l = ne[l]) { int v = e[l]; f[i][j][k] = (f[i][j][k] + f[i - 1][v][1 - k]) % mod; } } else { for(int l = h[j]; ~l; l = ne[l]) { int v = e[l]; f[i][j][k] = (f[i][j][k] + f[i - 1][v][k]) % mod; } } } } } printf("%lld\n", f[K][T][0]); return 0; }