题目链接:CF814E An unavoidable detour for home

题意:

给出 $n$ 个点,和每个点的度数 $d_i$,要求计数无向图满足:

  • $1$ 到 $i$ 的最短路有且仅有一条。
  • 设 $l_i$ 是 $1$ 到 $i$ 的最短路,则 $\forall i\in[1,n),l_i\le l_{i+1}$。

$3\le n\le 50,2\le d_i\le 3$。

首先由于 $l_i\le l_{i+1}$,容易意识到,对于 $l$ 相同的节点,他们的编号一定构成一个区间。容易发现,第 $2$ 层的节点编号一定是 $[2,d_1+1]$。

当我们划分了区间之后,第 $i$ 层可以向第 $i+1$ 层连边,第 $i$ 层内部可以连边,其他的边都是不合法的了。

现在考虑第 $i$ 层的连边情况,设从第 $i-1$ 层连下来了 $m$ 条边,这说明第 $i$ 层恰好有 $m$ 个节点。设其中 $2$ 度点有 $x$ 个,$3$ 度点有 $y$ 个,他们分别和上层连一条边,所以实际上是 $1$ 度点 $x$ 个,$2$ 度点 $y$ 个。

设该层内部有 $z$ 条边,那么给下层引出的边有 $x+2y-2z$ 条,他们可以任意排列,共 $(x+2y-2z)!$ 种方案。

再考虑层内部的 $z$ 条边,把 $2$ 度点看做两个点,则选出 $z$ 个不相交无序二元组的方案数是:

$$ \dfrac{(x+2y)!}{(x+2y-2z)!\times 2^z\times z!} $$

即首先选出 $2z$ 个数,相邻两两一对。除去每一对内部的顺序,除去 $z$ 个对之间的顺序。

但由于将 $2$ 度点看做了两个点,所以可能出现重边、自环的情况。考虑容斥,钦定出现了 $p$ 条重边,$q$ 个自环,那么从 $y$ 个 $2$ 度点中选出他们的方案数是:

$$ \dfrac{y!\times 2^p}{(y-2p-q)!\times p!\times q!\times 2^p} $$

即:

$$ \dfrac{y!}{(y-2p-q)!\times p!\times q!} $$

上面要乘一个 $2^p$ 是因为一组重边有两种连接方式,比如 $(1,2),(1',2')$ 和 $(1,2'),(1',2)$。由于在容斥,所以前面还有一个 $(-1)^{p+q}$ 的容斥系数。

设 $s=2p+q$,则已经选出了 $s$ 条边,$2s$ 个点。那么剩下的 $x+2y-2s$ 个点中,还需要选出 $z-s$ 条边,方案就是:

$$ \dfrac{(x+2y-2s)!}{(x+2y-2z)!\times 2^{z-s}\times (z-s)!} $$

因此在 $x$ 个 $1$ 度点,$y$ 个 $2$ 度点中,内部连 $z$ 条边,同时往下一层引出 $x+2y-2z$ 条边的贡献就是:

$$ \sum_{s=2p+q,s\le\min(y,z)} \frac{(-1)^{p+q}y!}{(y-s)! p! q!}\times \dfrac{(x+2y-2s)!}{(x+2y-2z)!2^{z-s} (z-s)!}\times (x+2y-2z)!\times \frac{1}{2^y} $$

最后一个要乘 $\dfrac{1}{2^y}$ 是因为 $y$ 个 $2$ 度点被认为是两个点,需要取消顺序。

设:

$$ v_s=\sum_{2p+q=s} \dfrac{(-1)^{p+q}}{p!\times q!} $$

那么上式可以化简为:

$$ \sum_{0\le s\le \min(y,z)} \dfrac{y!(x+2y-2s)!v_s}{(y-s)!2^y}\times \dfrac{1}{(z-s)!2^{z-s}} $$

于是就可以设 $\text{dp}_{i,j}$ 表示已经考虑了前 $i$ 个点,最后一层向下一层引出了 $j$ 条边,那么新的一层的节点就是 $i+1,i+2\cdots i+j$,设其中有 $x$ 个 $2$ 度点,$y$ 个 $3$ 度点。

那么可以通过枚举 $z$,再枚举 $s$,从 $\text{dp}_{i,j}$ 转移到 $\text{dp}_{i+j,x+2y-2z}$。这样就是 $\mathcal{O}(n^4)$ 的了。

考虑分部进行转移,我们发现 $z=s+(z-s)$,令 $d=z-s$,则可以先枚举 $s$,再枚举一次 $d$:

$$ \text{dp}_{i,j}\stackrel{s}{\longrightarrow} g_{i+j,x+2y-2s}\stackrel{d=z-s}{\longrightarrow} \text{dp}_{i+j,x+2y-2z} $$

于是就可以做到时间复杂度 $\mathcal{O}(n^3)$,空间复杂度 $\mathcal{O}(n^2)$。

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

//#define FILE
#define int long long
#define file(FILENAME) freopen(FILENAME".in", "r", stdin), freopen(FILENAME".out", "w", stdout)
#define randint(l, r) (rand() % ((r) - (l) + 1) + (l))
#define LINE() cout << "LINE = " << __LINE__ << endl
#define debug(x) cout << #x << " = " << x << endl
#define abs(x) ((x) < 0 ? (-(x)) : (x))
#define inv(x) qpow((x), mod - 2)
#define lowbit(x) ((x) & (-(x)))
#define ull unsigned long long
#define pii pair<int, int>
#define LL long long
#define mp make_pair
#define pb push_back
#define scd second
#define vec vector
#define fst first
#define endl '\n'

const int MAXN = 110;
const int INF = 2e9;
const double eps = 1e-6;
const double PI = acos(-1);
const int mod = 1e9 + 7;
//const int mod = 998244353;
//const int G = 3;
//const int base = 131;

int n, m, iv2 = (mod + 1) >> 1;
int d[MAXN], v[MAXN], dp[MAXN][MAXN], g[MAXN][MAXN];
int ip2[MAXN], fac[MAXN], ifac[MAXN];

template<typename T> inline bool read(T &a) {
    a = 0; char c = getchar(); int f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {a = a * 10 + (c ^ 48); c = getchar();}
    return a *= f, true;
}

template<typename A, typename ...B>
inline bool read(A &x, B &...y) {return read(x) && read(y...);}

int qpow(int x, int b) {
    int res = 1;
    for(; b; b >>= 1, (x *= x) %= mod) if(b & 1) (res *= x) %= mod;
    return res;
}

void init() {
    fac[0] = ifac[0] = ip2[0] = 1;
    for(int i = 1; i < MAXN; ++i) {
        fac[i] = fac[i - 1] * i % mod;
        ip2[i] = ip2[i - 1] * iv2 % mod;
    }
    ifac[MAXN - 1] = inv(fac[MAXN - 1]);
    for(int i = MAXN - 2; i; --i) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}

signed main () {
#ifdef FILE
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
#endif
    read(n); init();
    for(int i = 1; i <= n; ++i) read(d[i]);
    for(int s = 0; s <= n; ++s)
        for(int p = 0, q; (p << 1) <= s; ++p) {
            q = s - (p << 1);
            (v[s] += ifac[p] * ifac[q] % mod * (((p + q) & 1) ? mod - 1 : 1)) %= mod;
        }
    dp[1][d[1]] = 1;
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j <= (i << 1); ++j) {
            if(!g[i][j]) continue;
            for(int d = 0; (d << 1) <= j; ++d)
                (dp[i][j - (d << 1)] += g[i][j] * ip2[d] % mod * ifac[d]) %= mod;
        }
        for(int j = 1, x, y; j <= n - i; ++j) {
            if(!dp[i][j]) continue;
            x = 0, y = 0;
            for(int k = i + 1; k <= i + j; ++k) d[k] == 2 ? x++ : y++;
            for(int s = 0, coe; s <= y; ++s) {
                coe = fac[y] * fac[x + (y << 1) - (s << 1)] % mod * v[s] % mod * ifac[y - s] % mod * ip2[y] % mod;
                (g[i + j][x + (y << 1) - (s << 1)] += dp[i][j] * coe) %= mod;
            }
        }
    }
    printf("%lld\n", dp[n][0]);
    return 0;
}

标签: 图论, 计数, 最短路

添加新评论