Live2D

Solution -「CF 156D」Clues

Description

  link.
  给一个 n 个点 m 条边的无向图 G。设图上有 k 个连通块,求出添加 k1 条边使得这些连通块全部连通的方案数。对给定的 p 取模。
  n,m105

Solution

  Prufer 序列,设第 i 个连通块(可能是单点)的度数为 di,大小为 si。考虑连通块都是单点,方案数为:

(k2d11,d21,,dk1)

  即 k2 个可重元素的排列数。接下来考虑连通块的大小,每个连通块都可以选出一个点来连边。所以方案数应乘上 sidi。那么方案数:

(k2d11,d21,,dk1)i=1ksidi

  枚举 ti=di1

ti0ti=k2(k2t1,t2,,tk)i=1ksiti+1

  发现有一个 k 元多项式 i=1ksik2 次方,提出来:

(i=1ksi)k2i=1ksi

  显然 i=1ksi=n,所以答案:

nk2i=1ksi

Code

  为什么不直接打并查集啊喂。

#include <cstdio>
#include <vector>

const int MAXN = 1e5, MAXM = 1e5;
int n, m, p, ecnt, head[MAXN + 5];
std::vector<int> siz;
bool vis[MAXN + 5];

struct Edge { int to, nxt; } graph[MAXM * 2 + 5];

inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }, head[s] = ecnt; }

inline int qkpow ( int a, int b ) {
	int ret = 1;
	for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
	return ret;
}

inline int DFS ( const int u ) {
	if ( vis[u] ) return 0;
	int ret = vis[u] = true;
	for ( int i = head[u]; i; i = graph[i].nxt ) ret += DFS ( graph[i].to );
	return ret;
}

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