题解 -
题目意思
- 二项式反演题
我们按套路设
表示钦定有 个颜色数为 的方案数。 表示恰好有 个颜色数为 。
那么
关键怎么求这个
中间那一项可以这样子理解:你先要在 个位置中至少选出 个位置的方案数为 ,对于具体地填入可以理解为先按照全排列的方式填再去掉相同颜色的那些多余情况,即 。其他都很好理解。
时间复杂度: 由于跑不满可以过 的点
然后我们考虑优化是这个式子:
原式:
这就是道很经典的题目了,可以看看 力那道题目
我们设 ,
那么原式可以写成:
直接 一下就好了。
#include <bits/stdc++.h>
#define For(i,a,b) for ( int i=(a);i<=(b);i++ )
#define Dow(i,b,a) for ( int i=(b);i>=(a);i-- )
#define GO(i,x) for ( int i=head[x];i;i=e[i].nex )
#define mem(x,s) memset(x,s,sizeof(x))
#define cpy(x,s) memcpy(x,s,sizeof(x))
#define YES return puts("YES"),0
#define NO return puts("NO"),0
#define GG return puts("-1"),0
#define pb push_back
#define int long long
using namespace std;
inline int read()
{
int sum=0,ff=1; char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=getchar();
return sum*ff;
}
const int mod=1e9+7;
const int mo=1004535809;
const int N=1e7+5;
const int G=3ll;
int n,m,fac[N],inv[N],F[N],f[N],g[N];
int w[N],ans,l,r[N],mx,INV,lim,s;
inline int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1ll) ans=ans*a%mo;
a=a*a%mo;
b>>=1ll;
}
return ans%mo;
}
const int InvG=ksm(G,mo-2);
inline int C(int n,int m)
{
return fac[n]*inv[m]%mo*inv[n-m]%mo;
}
inline void NTT(int *f,int op)
{
For(i,0,lim-1) if(i<r[i]) swap(f[i],f[r[i]]);
for ( int mid=1;mid<lim;mid<<=1ll)
{
int tmp=ksm(G,(mo-1)/(mid*2ll));
if(op) tmp=ksm(tmp,mo-2);
for ( int i=0;i<lim;i+=mid*2ll )
{
int w=1ll;
for ( int j=0;j<mid;j++,w=w*tmp%mo )
{
int x=f[i+j];
int y=w*f[i+j+mid]%mo;
f[i+j]=(x+y)%mo;
f[i+j+mid]=(x-y+mo)%mo;
}
}
}
if(op) For(i,0,lim-1) f[i]=f[i]*INV%mo;
}
signed main()
{
n=read(),m=read(),s=read();
For(i,0,m) w[i]=read();
int M=max(n,m);
fac[0]=1;
For(i,1,M) fac[i]=fac[i-1]*i%mo;
inv[M]=ksm(fac[M],mo-2)%mo;
Dow(i,M-1,0) inv[i]=inv[i+1]*(i+1)%mo;
mx=min(m,n/s);
Dow(i,mx,0) F[i]=C(m,i)*fac[n]%mo*inv[n-i*s]%mo*ksm(ksm(fac[s],i),mo-2)%mo*ksm(m-i,n-i*s)%mo;
For(i,0,mx) f[i]=F[i]*fac[i]%mo;
For(i,0,mx) g[i]=((i&1ll)?mo-inv[i]:inv[i]);
reverse(f,f+mx+1);
lim=1ll;
while(lim<=(mx+1)<<1ll) lim<<=1ll,l++;
INV=ksm(lim,mo-2)%mo;
For(i,0,lim-1) r[i]=r[i>>1ll]>>1ll|((i&1ll)<<(l-1));
NTT(f,0),NTT(g,0);
For(i,0,lim-1) f[i]=f[i]*g[i]%mo;
NTT(f,1);
int ans=0;
For(i,0,mx) ans=(ans+1ll*f[mx-i]*w[i]%mo*inv[i]%mo)%mo;
printf("%lld\n",ans);
return 0;
}