连在一起的幻想乡

题目

题目描述

解法

这其实是套路题

(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;
}
原文地址:https://www.cnblogs.com/2016gdgzoi509/p/9673003.html