洛谷链接
显然颜色数量不会超过 lim=min(m,ns)\lim=\min(m,\frac ns)lim=min(m,sn)
fi:f_i:fi: 至少有 iii 种颜色恰好出现了 sss 次的方案数。
则 fi=(mi)⋅n!(s!)i(n−is)!⋅(m−i)n−isf_i=\binom mi·\frac{n!}{(s!)^i(n-is)!}·(m-i)^{n-is}fi=(im)⋅(s!)i(n−is)!n!⋅(m−i)n−is。
mmm 种颜色中选 iii 种,(mi)\binom mi(im)。
将 nnn 个位置分成 i+1i+1i+1 个部分,钦定的 iii 种颜色每种出现 sss 次,剩下 m−im-im−i 种颜色共 n−isn-isn−is 个。
先当作是可重的全排列,即 n!s!s!...s!⏟i(n−is)!\frac{n!}{\underbrace{s!s!...s!}_i(n-is)!}is!s!...s!(n−is)!n!。
前 iii 各部分都是只有一种颜色,后面部分每个有 m−im−im−i 种取法,所以要乘上 (m−i)n−is(m-i)^{n-is}(m−i)n−is。
ansi:ans_i:ansi: 恰有 iii 个颜色出现恰好 sss 次的方案数。
直接二项式反演:ansi=∑j=ilim(−1)j−i(ji)fjans_i=\sum_{j=i}^{\lim}(-1)^{j-i}\binom jif_jansi=∑j=ilim(−1)j−i(ij)fj。
ansi=∑j=ilim(−1)j−i(ji)fj=∑j=ilim(−1)j−1j!i!(j−i)!fj⇒ansi⋅i!=∑j=ilim(−1)j−i(j−i)!j!⋅fjans_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
令 Fi=i!⋅fi,Gi=(−1)ii!F_i=i!·f_i,G_i=\frac{(-1)^i}{i!}Fi=i!⋅fi,Gi=i!(−1)i,则 ansi=1i!∑j=ilimGj−iFj=1i!∑j=0lim−iGjFj+ians_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。
反转 FFF,即 Fi=(lim−i)!⋅flim−iF_i=(\lim-i)!·f_{\lim-i}Fi=(lim−i)!⋅flim−i,则 ansi=1i!∑j=0lim−iGjFlim−i−jans_i=\frac{1}{i!}\sum_{j=0}^{\lim-i}G_jF_{\lim-i-j}ansi=i!1∑j=0lim−iGjFlim−i−j。
可以卷了 G∗FG*FG∗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;
}