【题解】P3747 [六省联考2017]期末考试 (单位根反演)

【题解】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;
}



原文地址:https://www.cnblogs.com/winlere/p/12739234.html