BEST定理学习笔记

内容:

对于一张有向图,以 \(s\) 为起点,它的欧拉回路数量为

\[det_sdeg_s!\prod (deg_u-1)![u \neq s] = det_sdeg_s\prod (deg_u-1)! \]

\(det_s\) 表示以 \(s\) 为根的有根生成树个数 (可以是内向树也可以是外向树), \(deg_u\) 表示点 \(u\) 的度数(可以是入度也可以是出度).

证明:

对于一棵有根生成树,不妨设它是内向树,给它的每一条边标一个顺序,其中指向根的边顺序最靠后,方案数显然是 \(deg_s!\prod (deg_u-1)![u \neq s]\), 再乘上有根生成树的个数就是 \(det_sdeg_s!\prod (deg_u-1)![u \neq s]\), 接着就是证明每种标号方案对应一条欧拉回路.

每经过一条边后,删掉这条边,只需证明剩下的边存在欧拉路径,即剩下的边满足如下条件。

  1. 图弱联通
  2. 有两个点入度和出度相差 \(1\), 其它点入度等于出度

若删去的边 \((u, v)\) 是非树边,由于树边是每个点最后离开的边 \((u, v)\) 之间的树边一定还没有被删去,则 \(u\)\(v\) 之间可以直接通过树边形成弱连通;

若删去的边是树边,则删去后 \(u\) 与剩下的图完全断开,相当于删去了一条链末端的一条边,剩余边仍然弱连通。

所以删掉一条边后剩下的图仍然有欧拉路径.

Code:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int mod = 1e6 + 3;

const int N = 110;
int A[N][N];
int degin[N], degout[N];
int T, n;
int fac[300000], inv[300000], ifac[300000];

int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1ll * res * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return res;
}

int add(int a, int b) {
    return a + b >= mod ? a + b - mod : a + b;
}

int sub(int a, int b) {
    return a - b < 0 ? a - b + mod : a - b;
}

int det(int n) {
    int res = 1;
    for (int i = 2; i <= n; ++i) {
        if (!degout[i]) continue;
        if (!A[i][i]) {
            for (int j = i + 1; j <= n; ++j) {
                if (A[j][i]) {
                    swap(A[i], A[j]);
                    res = -res;
                    break;
                }
            }
        }
        for (int j = i + 1; j <= n; ++j) {
            int div = 1ll * A[j][i] * qpow(A[i][i], mod - 2) % mod;
            for (int k = i; k <= n; ++k) {
                A[j][k] = sub(A[j][k], 1ll * div * A[i][k] % mod);
            }
        }
    }
    res = (mod + res) % mod;
    for (int i = 2; i <= n; ++i) {
        res = 1ll * res * A[i][i] % mod;
    }
    return res;
}

signed main() {
    scanf("%d", &T);
    fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = 1;
    for (int i = 2; i <= 220000; ++i) {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
        ifac[i] = 1ll * ifac[i - 1] * inv[i] % mod;
    }
    while (T--) {
        scanf("%d", &n);
        memset(A, 0, sizeof(A));
        memset(degin, 0, sizeof(degin));
        memset(degout, 0, sizeof(degout));
        for (int i = 1; i <= n; ++i) {
            int s;
            scanf("%d", &s);
            for (int j = 1; j <= s; ++j) {
                int x;
                scanf("%d", &x);
                A[i][x] = sub(A[i][x], 1);
                ++degin[x];
                ++degout[i];
            }
            A[i][i] = add(A[i][i], s);
        }
        if (n == 1) {
            puts("1");
            continue;
        }
        int flag = 0;
        for (int i = 1; i <= n; ++i) {
            if (degin[i] != degout[i]) {
                flag = 1;
            }
        }
        if (flag == 1) {
            puts("0");
            continue;
        }
        int res = det(n);
        res = 1ll * res * fac[degout[1]] % mod;
        for (int i = 2; i <= n; ++i) {
            res = 1ll * res * fac[degout[i] - 1] % mod;
        }
        printf("%d\n", res);
    }
    return 0;
}
posted @ 2021-08-22 22:14  youwike  阅读(36)  评论(0编辑  收藏  举报