显然颜色数量不会超过 lim = min ( m , n s ) \lim=\min(m,\frac ns) lim=min(m,sn)
f i : f_i: fi: 至少有 i i i 种颜色恰好出现了 s s s 次的方案数。
则 f i = ( m i ) ⋅ n ! ( s ! ) i ( n − i s ) ! ⋅ ( m − i ) n − i s f_i=\binom mi·\frac{n!}{(s!)^i(n-is)!}·(m-i)^{n-is} fi=(im)⋅(s!)i(n−is)!n!⋅(m−i)n−is。
m m m 种颜色中选 i i i 种, ( m i ) \binom mi (im)。
将 n n n 个位置分成 i + 1 i+1 i+1 个部分,钦定的 i i i 种颜色每种出现 s s s 次,剩下 m − i m-i m−i 种颜色共 n − i s n-is n−is 个。
先当作是可重的全排列,即 n ! s ! s ! . . . s ! ⏟ i ( n − i s ) ! \frac{n!}{\underbrace{s!s!...s!}_i(n-is)!} i s!s!...s!(n−is)!n!。
前 i i i 各部分都是只有一种颜色,后面部分每个有 m − i m−i m−i 种取法,所以要乘上 ( m − i ) n − i s (m-i)^{n-is} (m−i)n−is。
a n s i : ans_i: ansi: 恰有 i i i 个颜色出现恰好 s s s 次的方案数。
直接二项式反演:
a
n
s
i
=
∑
j
=
i
lim
(
−
1
)
j
−
i
(
j
i
)
f
j
ans_i=\sum_{j=i}^{\lim}(-1)^{j-i}\binom jif_j
ansi=∑j=ilim(−1)j−i(ij)fj。
a
n
s
i
=
∑
j
=
i
lim
(
−
1
)
j
−
i
(
j
i
)
f
j
=
∑
j
=
i
lim
(
−
1
)
j
−
1
j
!
i
!
(
j
−
i
)
!
f
j
⇒
a
n
s
i
⋅
i
!
=
∑
j
=
i
lim
(
−
1
)
j
−
i
(
j
−
i
)
!
j
!
⋅
f
j
ans_i=\sum_{j=i}^{\lim}(-1)^{j-i}\binom jif_j=\sum_{j=i}^{\lim}(-1)^{j-1}\frac{j!}{i!(j-i)!}f_j\Rightarrow ans_i·i!=\sum_{j=i}^{\lim}\frac{(-1)^{j-i}}{(j-i)!}j!·f_j
ansi=j=i∑lim(−1)j−i(ij)fj=j=i∑lim(−1)j−1i!(j−i)!j!fj⇒ansi⋅i!=j=i∑lim(j−i)!(−1)j−ij!⋅fj
令
F
i
=
i
!
⋅
f
i
,
G
i
=
(
−
1
)
i
i
!
F_i=i!·f_i,G_i=\frac{(-1)^i}{i!}
Fi=i!⋅fi,Gi=i!(−1)i,则
a
n
s
i
=
1
i
!
∑
j
=
i
lim
G
j
−
i
F
j
=
1
i
!
∑
j
=
0
lim
−
i
G
j
F
j
+
i
ans_i=\frac{1}{i!}\sum_{j=i}^{\lim} G_{j-i}F_j=\frac{1}{i!}\sum_{j=0}^{\lim-i}G_jF_{j+i}
ansi=i!1∑j=ilimGj−iFj=i!1∑j=0lim−iGjFj+i。
反转 F F F,即 F i = ( lim − i ) ! ⋅ f lim − i F_i=(\lim-i)!·f_{\lim-i} Fi=(lim−i)!⋅flim−i,则 a n s i = 1 i ! ∑ j = 0 lim − i G j F lim − i − j ans_i=\frac{1}{i!}\sum_{j=0}^{\lim-i}G_jF_{\lim-i-j} ansi=i!1∑j=0lim−iGjFlim−i−j。
可以卷了 G ∗ F G*F G∗F, NTT \text{NTT} NTT 做即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 1004535809
#define maxn 10000005
#define maxm 500000
int fac[maxn], inv[maxn], r[maxm], W[maxm], f[maxm], g[maxm];
int len;
int qkpow( int x, int y ) {
int ans = 1;
while( y ) {
if( y & 1 ) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
void NTT( int *c, int op ) {
for( int i = 0;i < len;i ++ ) if( i < r[i] ) swap( c[i], c[r[i]] );
for( int i = 1;i < len;i <<= 1 ) {
int omega = qkpow( op == 1 ? 3 : mod / 3 + 1, (mod - 1) / (i << 1) );
for( int j = 0;j < len;j += (i << 1) )
for( int k = 0, w = 1;k < i;k ++, w = w * omega % mod ) {
int x = c[j + k], y = c[j + k + i] * w % mod;
c[j + k] = ( x + y ) % mod;
c[j + k + i] = ( x - y + mod ) % mod;
}
}
if( op == -1 ) {
int inv = qkpow( len, mod - 2 );
for( int i = 0;i < len;i ++ ) c[i] = c[i] * inv % mod;
}
}
void init( int n ) {
fac[0] = inv[0] = 1;
for( int i = 1;i <= n;i ++ ) fac[i] = fac[i - 1] * i % mod;
inv[n] = qkpow( fac[n], mod - 2 );
for( int i = n - 1;i;i -- ) inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}
int C( int n, int m ) { return fac[n] * inv[m] % mod * inv[n - m] % mod; }
signed main() {
int n, m, s;
scanf( "%lld %lld %lld", &n, &m, &s );
for( int i = 0;i <= m;i ++ ) scanf( "%lld", &W[i] );
init( max( n, m ) );
int lim = min( m, n / s );
for( int i = 0;i <= lim;i ++ ) {
f[i] = fac[i] * C(m, i) % mod * fac[n] % mod * qkpow(inv[s], i) % mod * inv[n - i * s] % mod * qkpow(m - i, n - i * s) % mod;
g[i] = (i & 1) ? mod - inv[i] : inv[i];
}
reverse( f, f + lim + 1 );
int l;
for( len = 1, l = 0;len <= (lim << 1);len <<= 1, l ++ );
for( int i = 0;i < len;i ++ ) r[i] = (r[i >> 1] >> 1) | ((i & 1) << l - 1 );
NTT( f, 1 ), NTT( g, 1 );
for( int i = 0;i < len;i ++ ) f[i] = f[i] * g[i] % mod;
NTT( f, -1 );
int ans = 0;
for( int i = 0;i <= lim;i ++ ) ( ans += W[i] * inv[i] % mod * f[lim - i] ) %= mod;
printf( "%lld\n", ans );
return 0;
}