CTS2019 珍珠

CTS2019 珍珠

题意:

给定 (n,m,D),计算有多少个长度为 (n) 的序列,每个元素的值为 ([1,D]),满足出现次数为奇数的颜色数小于 (n-2m) 的序列的数量。

(n,mle 10^9,Dle 10^5)

Solution


时隔一年,我来补这个题的原官方题解做法和 djq 神仙的解法了。

这个题目前大致有三种主流做法,今天打算集体补一下(容斥原本会的,打算重新推一下)

Algorithm1

  • 容斥

(f_i) 表示单子数量恰好为 (i) 的序列的数量。

(g_i) 表示单子数量至少为 (i) 的序列的数量。

根据容斥理论,我们有:

[g_i=sum_{jge i}f_jinom{j}{i} ]

于是有:

[egin{aligned} \&f_i=sum_{jge i}g_jinom{j}{i}(-1)^{j-i} \&Rightarrow f_i imes i!(-1)^i=sum_{j-k=i} g_jfrac{j!(-1)^j}{k!} end{aligned}]

于是执行一次差值卷积即可。

考虑 (g_i),不难注意到:

[egin{aligned} &g_k=inom{D}{k}Big(frac{e^x-e^{-x}}{2}Big)^ke^{(D-k)x}[x^n] imes n! \&Rightarrow inom{D}{k}frac{1}{2^k}sum_i inom{k}{i}(-1)^{k-i}e^{(2i+D)x}[x^n] imes n! \&Rightarrow inom{D}{k}frac{1}{2^k}sum_i frac{k!}{i!(k-i)!}(-1)^{k-i}(2i+D)^n) \&Rightarrow frac{D!}{(D-k)!2^k}sum_{i+j=k}frac{(2i+D)^n}{i!} imes frac{(-1)^j}{j!} end{aligned}]

可以卷积了。复杂度 (mathcal O(nlog n))


Algorithm2

原标算(大力生成函数,神仙组合意义转换,找规律,归纳)

(mleftarrow n-2m),这样我们的表述将更为简洁。

考虑通过生成函数将答案直接写出来,我们有:

[egin{aligned} &sum_{k=0}^{m}inom{D}{k}Big(frac{e^{x}-e^{-x}}{2}Big)^kBig(frac{e^x+e^{-x}}{2}Big)^{D-k}[x^n] imes n! \&Rightarrow frac{n!}{2^D}sum_{k=0}^m inom{D}{k}sum_i e^{2i-k}(-1)^{k-i}inom{k}{i}sum_j inom{D-k}{j}e^{2j-D+k}[x^n] \&Rightarrow frac{1}{2^D}sum_k^msum_isum_j inom{D}{k}inom{k}{i}inom{D-k}{j}(2i+2j-D)^n(-1)^{k-i} end{aligned}]

接下来考虑 (inom{D}{k}inom{k}{i}inom{D-k}{j}) 的组合意义。

等价于从 (D) 个球选 (k) 个再选 (i) 个,再从剩下的 (D-k) 个选 (j) 个的方案数。

也等价于先选 (i+j) 个出来,再选 (i) 个出来,剩下的归为 (j),再从 (D-(i+j)) 个中选 (k-i) 个出来。

于是有 (inom{D}{k}inom{k}{i}inom{D-k}{j}=inom{D}{i+j}inom{i+j}{i}inom{D-i-j}{k-i})

枚举 (i+j=t),回代得到:

[egin{aligned} &frac{1}{2^D}sum_{t}inom{D}{t}(2t-D)^nsum_{k}^msum_i inom{i+j}{i}inom{D-i-j}{k-i}(-1)^{k-i} end{aligned}]

考虑后面的组合数的组合意义,等价于从 ((0,0)) 出发走到 ((j,i)),再走到 ((D-k,k)) 处的方案数。

这样可以考虑通过生成函数来解决路径计数的问题,具体的,我们先会行走 (t) 步,每一步的贡献都是 (1),接下来我们走 (D-t) 步,每次走 (y) 贡献均为 (-1)

于是得到:

[egin{aligned} &frac{1}{2^D}sum_{t}inom{D}{t}(2t-D)^nsum_{k}^m (1+x)^t(1-x)^{D-t}[x^k] end{aligned}]

考虑设 (F(t,D)=sum_k^m (1+x)^t(1-x)^{D-t}[x^k])

我们发现 (F(0,d)=sum_k^m(1-x)^{d}[x^k]=sum_k^m inom{d}{k}(-1)^k)

通过手玩/人类智慧极限,我们发现 (F(0,0)=1,F(0,d)=(-1)^minom{d-1}{m})

具体论证可以通过帕斯卡定理来说明。

接下来我们进一步打表+手玩,发现 (F(x,y)=2F(x-1,y-1)-F(x-1,y))

知道结论后论证其正确性是 naive 的,但是正向推导是困难的,所以还是建议打表找规律。

不过事实上在计算 (F(1,6),F(2,6)...) 这些值的时候其实就可以看出来了。

那么设 (G_t(x)=sum_k F(t,k)x^k),不难发现 (G_t(x)=G_{t-1}(x)(2x-1))

于是我们有:

[G_t(x)=(2x-1)^tG_0(x) ]

其中,(G_0(x)) 是可以预处理的,设第 (i) 项系数为 (g_i)

于是我们有:

[egin{aligned} &F(t,D)=G_t(x)[x^D]=sum_k 2^k(-1)^{t-k}inom{t}{k}g_{D-k} \&=F(t,D)=t!sum_{k+j=t} frac{2^kg_{D-k}}{k!} imes frac{(-1)^j}{j!} end{aligned}]

一次 NTT 即可,复杂度 (mathcal O(nlog n))

我的实现不是很精细,大概是 (mathcal O(nlog P)) 的。

(Code:)

#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define re register
#define int long long
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int P = 998244353 ; 
const int Gi = 332748118 ;
const int G = 3 ; 
const int N = 4e5 + 5 ; 
int n, m, D, limit, Inv, L, R[N], f[N], A[N], B[N], f2[N], fac[N], inv[N] ; 
int fpow(int x, int k) {
	int ans = 1, base = x % P ;
	while(k) {
		if(k & 1) ans = 1ll * ans * base % P ;
		base = 1ll * base * base % P, k >>= 1 ;
	} return ans ;
}
void init(int x) {
	limit = 1, L = 0 ; while( limit < x ) limit <<= 1, ++ L ; 
	for(re int i = 0; i < limit; ++ i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)) ;
	Inv = fpow( limit, P - 2 ) ; 
}
void NTT(int *a, int type) {
	for(re int i = 0; i < limit; ++ i) if( R[i] > i ) swap( a[i], a[R[i]] ) ;
	for(re int k = 1; k < limit; k <<= 1 ) {
		int d = fpow( (type == 1) ? G : Gi, (P - 1) / (k << 1) ) ;
		for(re int i = 0; i < limit; i += (k << 1))
		for(re int j = i, g = 1; j < i + k; ++ j, g = g * d % P) {
			int nx = a[j], ny = a[j + k] * g % P ;
			a[j] = (nx + ny) % P, a[j + k] = (nx - ny + P) % P ; 
		}
	} if( !type ) rep( i, 0, limit ) a[i] = a[i] * Inv % P ; 
}
int C(int x, int y) {
	if( y > x ) return 0 ; 
	return fac[x] * inv[y] % P * inv[x - y] % P ;  
}
signed main()
{
	D = gi(), n = gi(), m = gi() ; fac[0] = inv[0] = 1 ; 
	m = n - 2 * m ; 
	if( m < 0 ) { puts("0") ; exit(0) ; } 
	rep( i, 1, D ) 
		fac[i] = fac[i - 1] * i % P, 
		inv[i] = fpow( fac[i], P - 2 ) ;
	f2[0] = f[0] = 1 ; 
	rep( i, 1, D ) f[i] = (m & 1) ? P - C(i - 1, m) : C(i - 1, m) ; 
	rep( i, 1, D ) f2[i] = f2[i - 1] * 2 % P ; 
	rep( i, 0, D ) A[i] = f[D - i] * f2[i] % P * inv[i] % P ;
	rep( i, 0, D ) B[i] = (i & 1) ? P - inv[i] : inv[i] ; 
	init(D + D + 5), NTT(A, 1), NTT(B, 1) ;
	rep( i, 0, limit ) A[i] = A[i] * B[i] % P ; 
	NTT(A, 0) ; int ans = 0 ;
	rep( i, 0, D ) f[i] = A[i] * fac[i] % P ; 
	rep( i, 0, D ) ans = (ans + C(D, i) * fpow(2 * i - D + P, n) % P * f[i] % P) % P ; 
	ans = ans * fpow(f2[D], P - 2) % P ;
	cout << ans << endl ; 
	return 0 ;
}

Algorithm3

djq 神仙的解法

同样 (mleftarrow n-2m)

考虑我们要求的其实是:

[sum_{k=0}^{m}inom{D}{k}Big(frac{e^{x}-e^{-x}}{2}Big)^kBig(frac{e^x+e^{-x}}{2}Big)^{D-k}[x^n] imes n! ]

[frac{1}{2^D}sum_{k=0}^{m}inom{D}{k}(e^{x}-e^{-x})^k(e^x+e^{-x})^{D-k}[x^n] imes n! ]

(Q_k(x)=inom{D}{k}(x-1)^k(x+1)^{D-k}),不难发现答案即 (frac{n!}{2^D}sum_{k=0}^D e^{-Dx}Q_k(e^{2x})[x^n])

假设我们知道了 (Q_k(x)) 的系数 (f^{(k)}_{i}),那么答案即为:

[2^{-D}sum_{k}^msum_i f^{(k)}_{i}(2i-D)^n ]

于是我们只需要维护 (Q(x)=sum_{k=0}^m Q_k(x)),这样答案即:

[2^{-D}sum_i f_i(2i-D)^n ]

注意到:

[egin{aligned} &Q(x)=sum_{k=0}^m Q_k(x) \&=sum_{k=0}^m inom{D}{k}(x-1)^k(x+1)^{D-k} end{aligned}]

根据万能并不的求导大法,我们有:

[egin{aligned} Q'(x)=sum_{k=0}^minom{D}{k}igg(k(x-1)^{k-1}(x+1)^{D-k}+(D-k)(x-1)^k(x+1)^{D-k-1}igg) end{aligned}]

于是:

[2Dcdot Q(x)-xQ'(x)= ]

好吧,求导有点考验耐心,我突然不想学习这个解法了。

原文地址:https://www.cnblogs.com/Soulist/p/13734000.html