题意:
给定 \(n,m,D\),计算有多少个长度为 \(n\) 的序列,每个元素的值为 \([1,D]\),满足出现次数为奇数的颜色数小于 \(n-2m\) 的序列的数量。
\(n,m\le 10^9,D\le 10^5\)
时隔一年,我来补这个题的原官方题解做法和 djq 神仙的解法了。
这个题目前大致有三种主流做法,今天打算集体补一下(容斥原本会的,打算重新推一下)
设 \(f_i\) 表示单子数量恰好为 \(i\) 的序列的数量。
设 \(g_i\) 表示单子数量至少为 \(i\) 的序列的数量。
根据容斥理论,我们有:
于是有:
于是执行一次差值卷积即可。
考虑 \(g_i\),不难注意到:
可以卷积了。复杂度 \(\mathcal O(n\log n)\)
原标算(大力生成函数,神仙组合意义转换,找规律,归纳)
\(m\leftarrow n-2m\),这样我们的表述将更为简洁。
考虑通过生成函数将答案直接写出来,我们有:
接下来考虑 \(\binom{D}{k}\binom{k}{i}\binom{D-k}{j}\) 的组合意义。
等价于从 \(D\) 个球选 \(k\) 个再选 \(i\) 个,再从剩下的 \(D-k\) 个选 \(j\) 个的方案数。
也等价于先选 \(i+j\) 个出来,再选 \(i\) 个出来,剩下的归为 \(j\),再从 \(D-(i+j)\) 个中选 \(k-i\) 个出来。
于是有 \(\binom{D}{k}\binom{k}{i}\binom{D-k}{j}=\binom{D}{i+j}\binom{i+j}{i}\binom{D-i-j}{k-i}\)
枚举 \(i+j=t\),回代得到:
考虑后面的组合数的组合意义,等价于从 \((0,0)\) 出发走到 \((j,i)\),再走到 \((D-k,k)\) 处的方案数。
这样可以考虑通过生成函数来解决路径计数的问题,具体的,我们先会行走 \(t\) 步,每一步的贡献都是 \(1\),接下来我们走 \(D-t\) 步,每次走 \(y\) 贡献均为 \(-1\)
于是得到:
考虑设 \(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 \binom{d}{k}(-1)^k\)
通过手玩/人类智慧极限,我们发现 \(F(0,0)=1,F(0,d)=(-1)^m\binom{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_0(x)\) 是可以预处理的,设第 \(i\) 项系数为 \(g_i\)。
于是我们有:
一次 NTT 即可,复杂度 \(\mathcal O(n\log n)\)
我的实现不是很精细,大概是 \(\mathcal O(n\log 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 ;
}
djq 神仙的解法
同样 \(m\leftarrow n-2m\)
考虑我们要求的其实是:
即
设 \(Q_k(x)=\binom{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}\),那么答案即为:
于是我们只需要维护 \(Q(x)=\sum_{k=0}^m Q_k(x)\),这样答案即:
注意到:
根据万能并不的求导大法,我们有:
于是:
好吧,求导有点考验耐心,我突然不想学习这个解法了。
原文:https://www.cnblogs.com/Soulist/p/13734000.html