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;
}
posted @ 2022-03-27 17:05  pbc的成长之路  阅读(26)  评论(0编辑  收藏  举报