Live2D

Solution -「HNOI 2009」「洛谷 P4727」图的同构计数

Description

  Link.

  求含 n 个点的无标号简单无向图的个数,答案模 997

Solution

  首先,把题目转化成为有标号 Kn(n2) 条边染黑(不选)白(选)两种颜色,求本质不同(去除标号)的方案数。想到使用 Pólya 定理求解。设在某个点转置中,循环大小为 a1,a2,,ak,分别考虑循环内部和循环间的边等价类:

  对于大小为 a 的循环内部,仍旧是一个 Ka,尝试求其中边等价类的个数,例如 a=6,如图(来自 @pythoner713 的博客,下图同):

可见共红色、绿色、蓝色三个等价类。进一步,若把 Ka 画作类似的正多边形,发现边属于同一等价类当且仅当它们长度相等,继而得出大小为 a 的循环的等价类个数为 a2

  对于两个大小分别为 a,b 的循环之间,考虑转置它们直到与初始状态重合,如图:

循环大小为 lcm(a,b),那么共 gcd(a,b) 个等价类。

  综上,符合 {ak} 所描述的转置的等价类个数 c(a)

i=1kai2+1i<jkgcd(ai,aj)

  考虑直接枚举 {ak},则符合条件的转置方案数为

n!i=1kaii=1nbi

其中 bi=j=1k[aj=i],即大小为 i 的循环个数。组合意义为:在所有转置中,固定每个循环的第一个元素,再去除等大循环的顺序。

  最终方案数为

1n!{ak}2c(a)n!i=1kaii=1nbi={ak}2c(a)(i=1kaii=1nbi)1

拆分 n 求解即可,复杂度见 A296010

Code

/* Clearink */

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

const int MOD = 997, MAXN = 60;
int n, fac[MAXN + 5];

inline int gcd( const int a, const int b ) { return b ? gcd( b, a % b ) : a; }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
inline int mpow( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

int a[MAXN + 5], ans, t;

inline int calc() {
	t += a[0] * a[0];
	int s = 1;
	for ( int l = 1, r; l <= a[0]; l = r ) {
		for ( r = l; r <= a[0] && a[l] == a[r]; ++r );
		s = mul( s, fac[r - l] );
	}

	int idx = 0;
	rep ( i, 1, a[0] ) {
		idx += a[i] >> 1, s = mul( s, a[i] );
		rep ( j, i + 1, a[0] ) idx += gcd( a[i], a[j] );
	}

	return mul( mpow( s, MOD - 2 ), mpow( 2, idx ) );
}

inline void split( const int x, const int rest ) {
	if ( !rest ) return addeq( ans, calc() );
	if ( rest < x ) return ;
	split( x + 1, rest );
	a[++a[0]] = x, split( x, rest - x ), --a[0];
}

int main() {
	scanf( "%d", &n );

	fac[0] = 1;
	rep ( i, 1, n ) fac[i] = mul( i, fac[i - 1] );

	split( 1, n );

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