【题解】P3747 [六省联考2017]组合数问题(单位根反演)
就是要我们求
[sum_{i=0}^k {nkchoose ik+r}
]
换一个形式
[=sum_{i=0}^{nk} {nkchoose i} [k|i-r]
]
单位根反演带入
[={1over k}sum_{i=0}^{nk} {nkchoose i} sum _{j=0}^{k-1} omega_k^{(i-r)j}
]
凑一个二项式
[={1over k}sum_{i=0}^{nk} sum_{j=0}^{k-1} {nkchoose i} omega_k^{ij}cdot omega_k^{-rj}
]
交换和式
[={1over k}sum_{j=0}^{k-1} omega_k^{-jr} sum_{i=0}^{nk} {nkchoose i} omega_k^{ij}
]
也就是
[{1over k}sum_{j=0}^{k-1} omega_k^{-jr} (1+omega_k^j)^{nk}
]
(这个形式是一个FFT的形式),令(f(x)=(1+x)^{nk}),原式即
[{1over k} sum_{j=0}^{k-1} omega_k^{-jr} f(omega_k^j)
]
考虑范德蒙德的逆矩阵,即
[V_k^{-1}={1 over k}egin{pmatrix}
{1}&{1}&{cdots}&{1}\
{1}&{omega_k^{-1}}&{cdots}&{omega_k^{-(k-1)}}\
{1}&{omega_k^{-2}}&{cdots}&{omega_k^{-(k-1)2}}\
{vdots}&{vdots}&{}&{vdots}\
{1}&{omega_k^{-(k-1)}}&{cdots}&{omega_k^{-(k-1)(k-1)}}\
end{pmatrix}
]
而(f(omega_k^j))是一个点值,而({1over k} sum_{j=0}^{k-1} omega_k^{-jr} f(omega_k^j)),就是:由点值构成的行向量([f(omega_k^0) quad f(omega_k^1)dots f(omega_k^{k-1})] imes V_{k}^{-1})的第(r)列的值!也就是(f(x))这个多项式循环卷积(因为我们带入的都是(k)次单位根,于是(f(x) mod x^k-1)意义下的)意义下第(r)项的值。(根据dft和idft的定义)
然后这里(k)很小,直接暴力卷积即可
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std; typedef long long ll;
typedef vector<int> poly;
int n,mod,k,r;
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
poly operator * (poly a,poly b){
if(a.empty()||b.empty()) return poly(k,0);
poly ret(k);
for(int t=0,ed=a.size();t<ed;++t)
for(int i=0,ed=b.size();i<ed;++i)
ret[(t+i)%k]=MOD(ret[(t+i)%k]+MOD(a[t],b[i]));
return ret;
}
poly ksm(ll x){
poly ret(k,0),b(k,0);
ret[0]=1; ++b[0]; ++b[1%k];
while(x){
if(x&1) ret=ret*b;
b=b*b,x>>=1;
}
return ret;
}
int main(){
cin>>n>>mod>>k>>r;
cout<<ksm(1ll*n*k)[r]<<endl;
return 0;
}