连在一起的幻想乡

题目

题目描述

解法

这其实是套路题

\(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;
}
posted @ 2018-09-19 10:11  EZ_WYC  阅读(188)  评论(0编辑  收藏  举报