zoukankan      html  css  js  c++  java
  • HihoCoder 1511: 树的方差(prufer序)

    题意

    对于一棵 (n) 个点的带标号无根树,设 (d[i]) 为点 (i) 的度数,定义一棵树的方差为数组 (d[1..n]) 的方差。

    给定 (n) ,求所有带标号的 (n) 个点的无根树的方差之和,答案对 (998244353) 取模。

    题解

    注意是方差之和,而不是方差的期望。

    首先方差有个套路转化,(displaystyle V[x]= E[x^2] -(E[x])^2) ,也就是平方的期望 减去期望的平方。

    此处可以把期望理解成加权平均数。

    至于原因?拆式子就好啦 qwq

    每条边会贡献到两个点,又由于点数等于边数加一,所以就有 (E[x] = displaystyle frac{2(n - 1)}{n})

    那么现在只需要求 (E[x^2]) 的期望就好了。

    如何算呢?看到 带标号无根树+度数 ,不难想到就是 ( ext{Prufer}) 序。

    ( ext{Prufer}) 序:

    一个 (n) 个结点的无根树,对应一个长度为 (n − 2) 、所有元素均为 ([1,n]) 内整数的序列,这个序列叫 ( ext{Prufer}) 序列。

    • 无根树 (⇒) ( ext{Prufer}) 序列:删除编号最小的叶子,将其邻点编号加入数列,持续这个过程直到图中只剩 (2) 个点。

    • ( ext{Prufer}) 序列 (⇒) 无根树:建立一个集合 ({1,2,...,n}) ,找出集合中最小的、未出现在 ( ext{Prufer}) 序列中的元素,将其与序列首项连边,并删去这个元素和序列首项,持续这个过程直到序列为空,然后把集合中最后两个数连边。

    • 点数为 (n) 的无根树个数 (=) 长度为 (n − 2)( ext{Prufer}) 序列个数 (= n^{n−2})

    • 无根树中一个点的度数 (=) 点的编号在 ( ext{Prufer}) 序列中出现次数 (+1)

    我们利用最后一条性质就可以做了,考虑枚举一个点在 ( ext{Prufer}) 序列的出现次数 (i) ,那么贡献就是

    [frac{1}{n}(sum_{i = 0}^{n - 2} {n - 2 choose i} n (n - 1)^{n - 2 - i} (i + 1)^2) ]

    意义是十分明显的,不要忘记除掉 (n) ,然后就能做完了。

    代码

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    
    using namespace std;
    
    template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
    
    inline int read() {
        int x(0), sgn(1); char ch(getchar());
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
        freopen ("1511.in", "r", stdin);
        freopen ("1511.out", "w", stdout);
    #endif
    }
    
    const int N = 1e6 + 1e3;
    
    int n, fac[N], ifac[N], Mod = 998244353;
    
    inline int fpm(int x, int power) {
    	int res = 1;
    	for (; power; power >>= 1, x = 1ll * x * x % Mod)
    		if (power & 1) res = 1ll * res * x % Mod;
    	return res;
    }
    
    void Math_Init(int maxn) {
    	fac[0] = ifac[0] = 1;
    	For (i, 1, maxn) fac[i] = 1ll * fac[i - 1] * i % Mod;
    	ifac[maxn] = fpm(fac[maxn], Mod - 2);
    	Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1ll) % Mod;
    }
    
    inline int Comb(int n, int m) {
    	if (n < 0 || m < 0 || n < m) return 0;
    	return 1ll * fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
    }
    
    int main() {
    
    	File();
    
    	n = read();
    
    	Math_Init(n);
    
    	int ans = 0;
    	For (i, 0, n - 2)
    		ans = (ans + 1ll * Comb(n - 2, i) % Mod * fpm(n - 1, n - 2 - i) % Mod * (i + 1) % Mod * (i + 1)) % Mod;
    	int Exp = 2ll * (n - 1) * fpm(n, Mod - 2) % Mod;
    	Exp = 1ll * Exp * Exp % Mod * fpm(n, n - 2) % Mod;
    	ans = (ans - Exp + Mod) % Mod;
    
    	printf ("%d
    ", ans);
    
    	return 0;
    
    }
    
  • 相关阅读:
    痞子衡嵌入式:i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计
    痞子衡嵌入式:嵌入式Cortex-M裸机环境下临界区保护的三种实现
    《痞子衡嵌入式半月刊》 第 36 期
    痞子衡嵌入式:嵌入式MCU中标准的三重中断控制设计
    痞子衡嵌入式:了解i.MXRTxxx系列ROM中灵活的串行NOR Flash启动硬复位引脚选择
    痞子衡嵌入式:串行NOR Flash的页编程模式对于量产效率的影响
    《痞子衡嵌入式半月刊》 第 35 期
    痞子衡嵌入式:对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同
    Springboot 配置文件、隐私数据脱敏的最佳实践(原理+源码)
    干掉 Postman?测试接口直接生成API文档,这个工具贼好用
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10300849.html
Copyright © 2011-2022 走看看