连在一起的幻想乡
题目
解法
这其实是套路题
设 \(h(n)\) 表示 \(n\) 个点的无向图的个数 \((\)不要求联通\()\), 则
\[h(n) = 2^{ n \choose 2 }
\]
设 \(g(n)\) 表示 \(n\) 个点的无向图的边数之和 \((\)不要求联通\()\), 则
\[g(n) = { n \choose 2 } \times 2^{ { n \choose 2 } - 1 }
\]
解释一下, 一条边可以别选中 $ 2 ^ { {n \choose 2} - 1 }$ 次
设 \(cnt(n)\) 表示 \(n\) 个点的无向图的边数的平方的和 \((\)不要求联通\()\), 则
\[cnt(n) = \sum_{i = 0}^{n - 1} { {n-1} \choose i } \bigg( cnt(n-1) + 2 \times i \times g(n-1) + i^2 \times h(n-1) \bigg)
\]
我们考虑枚举从 \(1\) 号点联出去的边, 再跟据 $ \left( x + \sum y \right)^2 = x^2 + 2 x \sum y + \sum y^2$, 就可以得出上边的式子
接下来我们要求联通后的值, 然后就很套路了, 每一步都是用不要求联通下的总和减去有多于两个联通块的值, 不细讲了
设 \(num(n)\) 表示 \(n\) 个点的联通无向图的个数, 则
\[num(n) = h(n) - \sum_{i = 1}^{n-1} {{n-1} \choose {i-1}} \times num(i)h(n-i)
\]
设 \(c(n)\) 表示 \(n\) 个点的联通无向图的边数的和, 则
\[c(n) = g(n) - \sum_{i = 1}^{n-1} {{n-1} \choose {i-1}} \times \bigg(num(i) g(n-i) + c(i) h(n-i) \bigg)
\]
设 \(f(n)\) 表示 \(n\) 个点的联通无向图的边数的平方的和, 则
\[f(n) = cnt(n) - \sum_{i = 1}^{n-1} {{n-1} \choose {i-1}} \times \bigg(num(i) cnt(n-i) + 2 c(i) g(n-i) + f(i) h(n-i) \bigg)
\]
\(g(n)\) 就是答案
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
inline LL power(LL a, LL n, LL mod)
{ LL Ans = 1;
a %= mod;
while (n)
{ if (n & 1) Ans = (Ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return Ans;
}
const int N = 2010;
LL mod;
inline LL Plus(LL a, LL b) { return a + b >= mod ? a + b - mod : a + b; }
inline LL Minus(LL a, LL b) { return a - b < 0 ? a - b + mod : a - b; }
LL h[N], g[N], cnt[N], num[N], c[N], f[N];
LL C[N][N];
int main()
{ int n;
scanf("%d %lld", &n, &mod);
C[0][0] = 1;
for (int i = 1; i <= n; i++)
{ C[i][0] = 1;
for (int j = 1; j <= i; j++)
C[i][j] = Plus(C[i-1][j-1], C[i-1][j]);
}
h[1] = 1;
for (int i = 2; i <= n; i++)
h[i] = power(2, i * (i-1) / 2, mod);
g[1] = 0;
for (int i = 2; i <= n; i++)
g[i] = (i * (i-1) / 2) * power(2, (i * (i-1) / 2) - 1, mod) % mod;
cnt[1] = 0;
for (int i = 2; i <= n; i++)
{ cnt[i] = 0;
for (int j = 0; j < i; j++)
cnt[i] = Plus(cnt[i], C[i-1][j] * Plus(cnt[i-1], Plus(2 * j * g[i-1] % mod, (LL)j * (LL)j % mod * h[i-1] % mod)) % mod);
}
num[0] = num[1] = 1;
for (int i = 2; i <= n; i++)
{ num[i] = h[i];
for (int j = 1; j < i; j++)
num[i] = Minus(num[i], C[i-1][j-1] * num[j] % mod * h[i - j] % mod);
}
for (int i = 2; i <= n; i++)
{ c[i] = g[i];
for (int j = 1; j < i; j++)
c[i] = Minus(c[i], C[i-1][j-1] * Plus(num[j] * g[i-j] % mod, c[j] * h[i-j] % mod) % mod);
}
for (int i = 2; i <= n; i++)
{ f[i] = cnt[i];
for (int j = 1; j < i; j++)
f[i] = Minus(f[i], C[i-1][j-1] * Plus(Plus(num[j] * cnt[i-j] % mod, 2ll * c[j] % mod * g[i-j] % mod), f[j] * h[i-j] % mod) % mod);
}
printf("%lld\n", (f[n] + mod) % mod);
return 0;
}