• 连在一起的幻想乡


    题目

    题目描述

    解法

    这其实是套路题

    (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;
    }
    
  • 相关阅读:
    java 实现前面带零的数字进行加减运算(保留前面的0)
    java 判断字符串是否是整数(纯数字:正整数、负整数、0)、至少包含一位小数、数字(可以是整数或小数)
    java 抽象类使用@Autowrited注入对象,子类直接使用父类的该属性
    java、springboot使用proguard混淆代码
    idea 使用阿里云仓库下载的jar包出现证书校验问题(PKIX:unable to find valid certification path to requested target)
    windows10安装zookeeper-3.6.2并生成zookeeper服务
    zookeeper-3.4.8 集群搭建
    zookeeper安装和使用 windows环境
    Dubbo入门---搭建一个最简单的Demo框架
    服务端高并发分布式架构演进之路
  • 原文地址:https://www.cnblogs.com/2016gdgzoi509/p/9673003.html
Copyright © 2020-2023  润新知