P4491 [HAOI2018]染色
出題人用心險惡啊。。。特意空格強調了“恰好”二字
如果恰 好出現了 \(S\) 次的顏色有 \(K\) 種
這題裡有兩個“恰好”,一個是恰好出現了 \(S\) 次,一個是恰好 \(K\) 種顏色。
這種一般都是轉成至多至少反演或者容斥。
容斥掉的根本不是出題人強調的那個恰好。。。
一開始我就進坑了,不過還好沒幾分鐘跳了出來就想到了下面的做法
稍微想一想就知道我們應該算的是:\(f(k)=\rm 至少 k 種顏色恰好出現 S 次\) 的方案數。
然而其實會算重的,並不嚴格是方案數,只不過重複是可以計算的。事實上二項式反演好像都是這麼幹的,非常巧妙。
這個 \(f\)
首先選出 \(k\) 種顏色,欽定出現 \(S\) 次,其餘亂選,這樣必然至少 \(k\) 種顏色出現 \(S\) 次。
這樣會欽定 \(Sk\) 個位置,那麼直接選好即為 \(\binom{n}{Sk}\) 。同時這些元素可以互換位置,那麼就是全排列除掉每一種顏色內部換順序的情況。剩下 \(n-Sk\) 個位置,每一個位置都有 \(m-k\) 種顏色可選。
把分子那個二項式係數拆成階乘,稍微化簡一下得
\[f(k)=\binom{m}{k}\dfrac{n!}{(S!)^{k}(n-Sk)!}(m-k)^{n-Sk} \]設恰好選 \(k\) 種顏色的方案數設為 \(g(k)\) , \(up=\min(m,\lfloor\dfrac{n}{S}\rfloor)\) 是選的顏色種數上界。
這個 \(g\) 是真的方案數了
再來說上面插的那句話
然而其實會算重的,並不嚴格是方案數
(這個其實是翻command_block的blog時發現的問題,剛開始學二項式反演被那個錯誤的叫法迷惑了好久,到現在才解決,雖然記個式子就能做題了。向cmd表示感謝。)
那麼究竟重複了多少呢?
首先肯定,重複的部分是亂選與欽定出現了相同的方案。
那麼對於 \(i(i\ge k)\) 種顏色相同,會在欽定 \(k\) 種相同時重複 \(\dbinom{i}{k}\)
比如 \(i=4,k=2\) , \(\{1,2,3,4\}\) 這個集合會在欽定 \(\{1,2\},\{1,3\},\{1,4\},\{2,3\},\{2,4\},\{3,4\}\) 的時候分別被計算一次。
所以:
\[f(k)=\sum_{i=k}^{up}\binom{i}{k}g(i) \]二項式反演得
\[g(k)=\sum_{i=k}^{up}(-1)^{i-k}\binom{i}{k}f(i) \]這個式子得想個辦法捲起來,沒法化了,而且看著很可卷的樣子。
卷王能一眼看出這個怎麼卷,我這種菜雞隻能慢慢試。。。
如果您是卷王可以直接跳過下面這部分。
組合數肯定得拆
\[g(k)=\dfrac{1}{k!}\sum_{i=k}^{up}f(i)i!\dfrac{(-1)^{i-k}}{(i-k)!} \]令 \(A(i)=f(i)i!,B(i)=\dfrac{(-1)^{i}}{i!}\)
\[g(k)=\dfrac{1}{k!}\sum_{i=k}^{up}A(i)B(i-k) \]必然要翻轉一個。翻轉哪個好呢?試一試就知道應該翻轉 \(A\) ,因為這樣下標從 \(0\) 開始才能卷。
那麼
\[g(k)=\dfrac{1}{k!}\sum_{i=k}^{up}A'(up-i)B(i-k)\\ =\dfrac{1}{k!}\sum_{i=0}^{up-k}A'(up-i-k)B(i) \]總算可以捲了/ll,卷完 \([x^k]g(x)\) 在 \(up-k\) 處提取。
出題人挺良心的,樣例看著很強的樣子=_=
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f?x:-x;
}
#define mod 1004535809
const int V=10000005;
const int N=100005;
const int M=N<<2;
namespace math{
int fac[V],ifc[V];
inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
inline void fmod(int&x){x-=mod,x+=x>>31&mod;}
int binom(int n,int m){return n<m?0:1ll*fac[n]*ifc[m]%mod*ifc[n-m]%mod;}
void initmath(const int&n=V-1){
fac[0]=1;for(int i=1;i<=n;++i)fac[i]=1ll*i*fac[i-1]%mod;
ifc[n]=qpow(fac[n],mod-2);for(int i=n-1;i>=0;--i)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
}
}
using math::qpow;
using math::fmod;
namespace poly{
int rev[M],lg,lim;
void poly_init(const int&n){
for(lg=0,lim=1;lim<n;++lg,lim<<=1);
for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int*a,int op){
for(int i=0;i<lim;++i)
if(i>rev[i])swap(a[i],a[rev[i]]);
const int g=op?3:qpow(3,mod-2);
for(int i=1;i<lim;i<<=1){
const int wn=qpow(g,(mod-1)/(i<<1));
for(int j=0;j<lim;j+=i<<1){
int w0=1;
for(int k=0;k<i;++k,w0=1ll*w0*wn%mod){
const int X=a[j+k],Y=1ll*a[i+j+k]*w0%mod;
fmod(a[j+k]=X+Y),fmod(a[i+j+k]=mod+X-Y);
}
}
}
if(op)return;const int ilim=qpow(lim,mod-2);
for(int i=0;i<lim;++i)a[i]=1ll*a[i]*ilim%mod;
}
#define clr(a,n) memset(a,0,sizeof(int)*(n))
#define cpy(a,b,n) memcpy(a,b,sizeof(int)*(n))
void poly_mul(int*f,int*g,int*ans,int n,int m){
static int A[M],B[M];poly_init(n+m);
cpy(A,f,n),clr(A+n,lim-n),NTT(A,1);
cpy(B,g,m),clr(B+m,lim-m),NTT(B,1);
for(int i=0;i<lim;++i)ans[i]=1ll*A[i]*B[i]%mod;
NTT(ans,0);
}
}
int n,m,S,up,f[M],g[M],A[M],B[M],C[M],W[N],ans;
signed main(){
math::initmath();
n=read(),m=read(),S=read(),up=min(n/S,m);
rep(i,0,m)W[i]=read();
for(int i=0;i<=up;++i)f[i]=1ll*math::binom(m,i)*math::fac[n]%mod*qpow(math::ifc[S],i)%mod*math::ifc[n-S*i]%mod*qpow(m-i,n-S*i)%mod;
for(int i=0;i<=up;++i)A[i]=1ll*math::fac[up-i]*f[up-i]%mod;
for(int i=0;i<=up;++i)B[i]=(i&1)?mod-math::ifc[i]:math::ifc[i];
poly::poly_mul(A,B,C,up+1,up+1);
for(int i=0;i<=up;++i)g[i]=1ll*math::ifc[i]*C[up-i]%mod;
for(int i=0;i<=up;++i)fmod(ans+=1ll*g[i]*W[i]%mod);
printf("%d\n",ans);
return 0;
}