Live2D

Solution -「HDU 5498」Tree

Description

  link.
  给定一个 n 个结点 m 条边的无向图,q 次操作每次随机选出一条边。问 q 条边去重后构成生成树的方案总数,对 p 取模。

Solution

  首先求出 n1 条边构成生成树的方案数,显然矩阵树定理。
  接着,令 f(i,j) 表示操作 i 次,去重后有 j 条边的方案数。那么有:

f(i,j)=jf(i1,j)+(mj+1)f(i1,j1)

  这个式子可以矩阵快速幂优化,最后把上面两个东西乘起来就是总方案啦。复杂度 O(n3logq)

Code

  这个 HDU 它一直 SF 呢 qwq。理论 AC 代码如下 w。

#include <cstdio>
#include <cstring>
#include <assert.h>
#include <iostream>

const int MAXN = 100;
int n, m, p, q, K[MAXN + 5][MAXN + 5];

inline void add ( const int u, const int v ) {
	++ K[u][u], ++ K[v][v], -- K[u][v], -- K[v][u];
	if ( K[u][v] < 0 ) K[u][v] += p;
	if ( K[v][u] < 0 ) K[v][u] += p;
}

inline int det ( const int n ) {
	int ret = 1, swp = 1;
	for ( int i = 1; i < n; ++ i ) {
		for ( int j = i + 1; j < n; ++ j ) {
			for ( ; K[j][i]; std::swap ( K[i], K[j] ), swp *= -1 ) {
				int d = K[i][i] / K[j][i];
				for ( int k = i; k < n; ++ k ) K[i][k] = ( K[i][k] - 1ll * d * K[j][k] % p + p ) % p;
			}
		}
		if ( ! ( ret = 1ll * ret * K[i][i] % p ) ) return 0;
	}
	return ( ret * swp + p ) % p;
}

struct Matrix {
	int n, m, mat[MAXN + 5][MAXN + 5];
	Matrix () {} Matrix ( const int tn, const int tm ): n ( tn ), m ( tm ), mat {} {}
	inline int* operator [] ( const int key ) { return mat[key]; }
	inline Matrix operator * ( Matrix t ) {
		assert ( m == t.n );
		Matrix ret ( n, t.m );
		for ( int i = 0; i <= n; ++ i ) {
			for ( int k = 0; k <= m; ++ k ) {
				for ( int j = 0; j <= t.m; ++ j ) {
					ret[i][j] = ( ret[i][j] + 1ll * mat[i][k] * t[k][j] ) % p;
				}
			}
		}
		return ret;
	}
};

inline Matrix qkpow ( Matrix A, int b ) {
	Matrix ret ( A.n, A.m );
	for ( int i = 0; i <= A.n; ++ i ) ret[i][i] = 1;
	for ( ; b; A = A * A, b >>= 1 ) if ( b & 1 ) ret = ret * A;
	return ret;
}

int main () {
	int T;
	for ( scanf ( "%d", &T ); T --; memset ( K, 0, sizeof K ) ) {
		scanf ( "%d %d %d %d", &n, &m, &p, &q );
		for ( int i = 1, u, v; i <= m; ++ i ) scanf ( "%d %d", &u, &v ), add ( u, v );
		int tree = det ( n );
		if ( ! tree || q < n - 1 ) { puts ( "0" ); continue; }
		Matrix F ( 0, n - 1 ), T ( n - 1, n - 1 );
		F[0][0] = 1;
		for ( int i = 0; i < n; ++ i ) {
			T[i][i] = i;
			if ( i ) T[i - 1][i] = n - i;
		}
		F = F * qkpow ( T, q );
		printf ( "%d\n", int ( 1ll * tree * F[0][n - 1] % p ) );
	}
	return 0;
}
posted @   Rainybunny  阅读(119)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示