题目
解法
这其实是套路题
设 (h(n)) 表示 (n) 个点的无向图的个数 (()不要求联通()), 则
[h(n) = 2^{ n choose 2 }
]
设 (g(n)) 表示 (n) 个点的无向图的边数之和 (()不要求联通()), 则
[g(n) = { n choose 2 } imes 2^{ { n choose 2 } - 1 }
]
解释一下, 一条边可以别选中 $ 2 ^ { {n choose 2} - 1 }$ 次
设 (cnt(n)) 表示 (n) 个点的无向图的边数的平方的和 (()不要求联通()), 则
[cnt(n) = sum_{i = 0}^{n - 1} { {n-1} choose i } igg( cnt(n-1) + 2 imes i imes g(n-1) + i^2 imes h(n-1) igg)
]
我们考虑枚举从 (1) 号点联出去的边, 再跟据 $ left( x + sum y ight)^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}} imes num(i)h(n-i)
]
设 (c(n)) 表示 (n) 个点的联通无向图的边数的和, 则
[c(n) = g(n) - sum_{i = 1}^{n-1} {{n-1} choose {i-1}} imes igg(num(i) g(n-i) + c(i) h(n-i) igg)
]
设 (f(n)) 表示 (n) 个点的联通无向图的边数的平方的和, 则
[f(n) = cnt(n) - sum_{i = 1}^{n-1} {{n-1} choose {i-1}} imes igg(num(i) cnt(n-i) + 2 c(i) g(n-i) + f(i) h(n-i) igg)
]
(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
", (f[n] + mod) % mod);
return 0;
}