Live2D

Solution -「UOJ #450」复读机

Description

  Link.

  求从 m 种颜色,每种颜色无限多的小球里选 n 个构成排列,使得每种颜色出现次数为 d 的倍数的排列方案数,对 19491001 取模。

  n109

  • m103d=3

  • m5×105d2

Solution

  分 d=1,2,3 求解。

  当 d=1,每个位置 m 种方案,答案为 mn

  当 d=2,偶数序列的 EGF 为 G(x)=i=0+x2i(2i)!=ex+ex2,那么答案为:

n![xn]Gm(x)=n![xn](ex+ex2)m=n!2m[xn](i=0m(mi)e(2im)x)=2mi=0m(mi)(2im)n

  第二步到第三步用到常见的 eax=i=0+aii!xi。此时就能 O(mlogn) 求出答案了。

  当 d=33 的倍数数的 EGF 为 G(x)=i=0+[3|i]xii!,这个不太好算,来一发单位根反演:

G(x)=i=0+[3|i]xii!=13i=0+xii!j=02ω3ij=13j=02i=0+(ω3jx)ii!=13j=02eω3jx

  接着求答案,暴力展开三项式幂:

n![xn]Gm(x)=n!3m[xn](j=02eω3jx)m=n!3m[xn](a+b+c=m(ma,b,c)e(aω30+bω31+cω32)x)=3ma+b+c=m(ma,b,c)(aω30+bω31+cω32)n

  注意到 c=mab,所以多重组合数的值就是 m!a!b!c!,该式能在 O(m2logn) 的时间内算出。实际上该式就是 d=2 的情况的扩展,由于 ω20,1=±1,所以亦能从该式推回 d=2 的情况。

Code

/* Clearink */

#include <cstdio>

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

const int MOD = 19491001, MAXM = 5e5, INV2 = MOD + 1 >> 1, INV3 = 12994001;
const int W[] = { 1, 663067, 18827933 };
int n, m, d, fac[MAXM + 5], ifac[MAXM + 5];

inline int mul ( long long a, const int b ) { return a * b % MOD; }
inline int sub ( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int add ( int a, const int b ) { return ( a += b ) < MOD ? a : 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;
}

inline void init () {
	fac[0] = 1;
	rep ( i, 1, m ) fac[i] = mul ( i, fac[i - 1] );
	ifac[m] = mpow ( fac[m], MOD - 2 );
	per ( i, m - 1, 0 ) ifac[i] = mul ( i + 1, ifac[i + 1] );
}

inline int comb ( const int n, const int m ) {
	return n < m ? 0 : mul ( fac[n], mul ( ifac[m], ifac[n - m] ) );
}

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