首页 > 其他分享 >Luogu 5296 生成树计数

Luogu 5296 生成树计数

时间:2023-07-20 18:36:51浏览次数:31  
标签:ch limits 计数 5296 Luogu sum int define mod

好像有道题是求生成树权值和的和的,考虑 \(\sum\limits_{T}\sum\limits_{e\in E(T)}w_e\) 咋做。

每条边给一个边权 \(v_e(x)=1+w_ex\),然后跑矩阵树:

\[\text{ans}=[x]\sum\limits_{T}\prod\limits_{e\in E(T)}v_e(x) \]

受到来自神秘力量的启示,我们考虑类似上面这样构造边权。

考虑两个边权 \(a,b\),构造一个乘法运算,能把 \((a+b)^k\) 弄出来:

\[\begin{aligned}(a+b)^k&=\sum\limits_{i}\dbinom{k}{i}a^ib^{k-i}\\&=\sum\limits_{i}\frac{k!}{i!(k-i)!}a^ib^{k-i}\\\frac{(a+b)^k}{k!}&=\sum\limits_{i}\frac{a^i}{i!}\times \frac{b^{k-i}}{(k-i)!}\\\left[x^k\right]e^{(a+b)x}&=[x^k]e^{ax}\times e^{bx}\end{aligned} \]

其实这就是两个 EGF 的乘法。

所以每条边的边权就是多项式 \(v_e(x)=e^{w_ex}\),维护前 \(k+1\) 项(\(x^0\to x^k\))即可。高斯消元求行列式的时候注意求逆需要零次项不为 \(0\)。

#include <bits/stdc++.h>
using namespace std;

namespace vbzIO {
	char ibuf[(1 << 20) + 1], *iS, *iT;
	#if ONLINE_JUDGE
	#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
	#else
	#define gh() getchar()
	#endif
	#define rd read
	#define wr write
	#define pc putchar
	#define pi pair<int, int>
	#define mp make_pair
	#define fi first
	#define se second
	#define pb push_back
	#define ins insert
	#define era erase
	inline int read () {
		char ch = gh();
		int x = 0;
		bool t = 0;
		while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
		while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
		return t ? ~(x - 1) : x;
	}
	inline void write(int x) {
		if (x < 0) {
			x = ~(x - 1);
			putchar('-');
		}
		if (x > 9)
			write(x / 10);
		putchar(x % 10 + '0');
	}
}
using vbzIO::read;
using vbzIO::write;

const int N = 35;
const int mod = 998244353;
int n, k, e[N][N], fac[N], ifac[N], inv[N];

void init(int l) {
	fac[0] = ifac[0] = inv[1] = 1;
	for (int i = 1; i <= l; i++) {
		if (i > 1) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
		fac[i] = 1ll * fac[i - 1] * i % mod, ifac[i] = 1ll * ifac[i - 1] * inv[i] % mod; 
	}
}

int qpow(int p, int q) {
	int res = 1;
	while (q) {
		if (q & 1) res = 1ll * res * p % mod;
		p = 1ll * p * p % mod, q >>= 1;
	}
	return res;
}

struct ply {
	int w[N];
	ply () { memset(w, 0, sizeof(w)); }
	void mk(int v) {
		for (int i = 0, t = 1; i <= k; i++, t = 1ll * t * v % mod) 
			w[i] = 1ll * ifac[i] * t % mod;
	}
	ply operator + (const ply &rhs) const {
		ply res;
		for (int i = 0; i <= k; i++) 
			res.w[i] = (w[i] + rhs.w[i]) % mod;
		return res;
	}
	ply operator - (const ply &rhs) const {
		ply res;
		for (int i = 0; i <= k; i++) 
			res.w[i] = (w[i] - rhs.w[i] + mod) % mod;
		return res;
	}
	ply operator * (const ply &rhs) const {
		ply res;
		for (int i = 0; i <= k; i++) 
			for (int j = 0; j <= i; j++)
				(res.w[i] += 1ll * w[j] * rhs.w[i - j] % mod) %= mod;
		return res;
	}
	ply inv() {
		ply res; res.w[0] = qpow(w[0], mod - 2);
		for (int i = 1; i <= k; i++) {
			for (int j = 0; j < i; j++) 
				(res.w[i] += (mod - 1ll * res.w[j] * w[i - j] % mod)) %= mod;
			res.w[i] = 1ll * res.w[i] * res.w[0] % mod;
		}
		return res;
	}
} a[N][N], zr;

ply det() {
	ply res; res.mk(0);
	res = res.inv();
	for (int i = 1; i <= n; i++) {
		int p = i;
		while (p <= n && !a[p][i].w[0]) p++;
		swap(a[i], a[p]);
		if (i ^ p) res = zr - res;
		res = res * a[i][i];
		ply iv = a[i][i].inv();
		for (int j = i + 1; j <= n; j++) 
			a[i][j] = a[i][j] * iv;
		for (int j = 1; j <= n; j++) {
			if (i == j) continue;
			for (int k = i + 1; k <= n; k++) 
				a[j][k] = a[j][k] - a[i][k] * a[j][i];
		}
	}
	return res;
}

int main() {
	n = rd(), k = rd(), init(k);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			e[i][j] = rd();
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			ply tp; tp.mk(e[i][j]);
			a[i][i] = a[i][i] + tp;
			a[j][j] = a[j][j] + tp;
			a[i][j] = a[i][j] - tp;
			a[j][i] = a[j][i] - tp;
		}
	}
	n--;
	ply res = det();
	wr(1ll * res.w[k] * fac[k] % mod);
	return 0;
}

标签:ch,limits,计数,5296,Luogu,sum,int,define,mod
From: https://www.cnblogs.com/Ender32k/p/17569323.html

相关文章

  • Luogu 3412 仓鼠找sugar II
    你也许说得对,但我是真看不懂第一篇题解那个答案式子……预处理是差不多的。设\(f_u\)表示从\(u\tofa(u)\)的期望步数,\(g_u\)为\(fa(u)\tou\)的期望步数,\(d_u\)为\(u\)的度数。那么显然有:\[f_u=\frac{1}{d_u}\left(1+\sum\limits_{v\inson(u)}(1+f_v+f_u)\right)......
  • 【题解】Luogu[P3360] 偷天换日
    solution开题显然是个树形dp,只不过在树形dp上又增加了背包问题。我们不妨将每个走廊看成一个点,把交叉口看成边(当然也可以把交叉口看成点,不过写起来麻烦一些),于是就转化为了一棵二叉树。我们设\(f_{i,j}\)表示以\(i\)为根的子树内,花费了不超过\(j\)时间,能拿到的最大价值......
  • 【题解】Luogu[P2607] [ZJOI2008] 骑士
    题目说给定\(n\)个点\(n\)个关系,也就是\(n\)条边,显然是基环树,又因为没有规定一定连通,于是我们可以将题目简化为给定一个基环树森林,点有点权,相邻的两个点不能同时选,问最大点权和。part1我们先考虑如果没有环,只是树,该怎么做。这一部分很简单,令\(f_{i,0/1}\)表示以\(i\)......
  • Burnside定理和Polya计数
    置换群Burnside定理和Polya计数都需要运用置换群的知识置换群主要有三种运算,分别是合成运算、恒等置换、置换的逆运用着三种运算就可以推导出Burnside定理和Polya计数的公式Burnside定理Burnside定理的主要应用是循环排列计数、项链计数、正五角形着色等下面给出一道例题P......
  • JVM程序计数器
    JVM程序计数器1.介绍JVM中的程序计数寄存器(ProqramCounterRegister)中,Reqister的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序......
  • 常用的统计数学函数:sum, sd, mean, cv
    /************************************************************************@filemath.h*@ingroupmath*@authorwangqing*@date2020-05-14*@brief常用统计计算***********************************************************************/#ifndef__MATH_......
  • [刷题笔记] Luogu P1168 中位数
    ProblemDescription题目描述非常简洁,不作解释。Solution题目要求对前奇数项求中位数?朴素的做法是暴力,但是范围1e5显然T。这里主要介绍一种堆顶堆的做法。堆顶堆是什么呢?我们不妨开两个堆,一个大根堆一个小根堆。动态维护中位数,初始令前1位的中位数为\(a_i\),遍历数组,若遇到比中......
  • 洛谷 Luogu P1038 [NOIP2003 提高组] 神经网络
    这题看着很吓人实则很简单。求输出层,正着求很麻烦,因为知不道谁连向这个点,所以可以反向建边,反着求。拓扑+dfs,时间复杂度\(\text{O(n+m)}\)#include<iostream>#include<cstdio>#include<queue>#defineN105#defineM(N*N/2+114)structE{intv,w;......
  • 【HMS Core】Health Kit 步数数据查询步骤咨询,血压/血氧的原子采样统计数据类型问题咨
    ​【问题描述】1、在进行步数查询---多日统计数据查询的时候,postman测试,发现了采样数据类型不匹配问题多日统计查询时,数据类型为 "com.huawei.continuous.steps.total"报错。反而数据类型为明细采样数据类型时“com.huawei.continuous.steps.delta”,正常返回。2、血压/血氧的......
  • 求质数:204. 计数质数
    https://leetcode.cn/problems/count-primes/204给定整数n,返回所有小于非负整数n的质数的数量。示例1:输入:n=10输出:4解释:小于10的质数一共有4个,它们是2,3,5,7。这题如果用对每个数i,让j从2遍历至sqrt(i)是否存在j,使得i%j==0(被j整除),是最容易的想到的,......