1. 程式人生 > 其它 >King Bombee(圖上dp)

King Bombee(圖上dp)

題意

給定一個\(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\)

”狀態,將偶數看作是“\(0\)”狀態。那麼我們改進一下我們的轉移陣列。
\(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;
}