传送门
题意:
∑i=0⌊nk⌋(nik)Fik\sum_{i=0}^{\lfloor\frac nk\rfloor}\binom n{ik}F_{ik}i=0∑⌊kn⌋(ikn)Fik
FFF为斐波拉契数列
n≤1e18,k≤2e4,p≤1e9n\leq 1e18,k\leq2e4,p\leq1e9n≤1e18,k≤2e4,p≤1e9且为质数且模kkk余111
显然就是求
∑i=0n[k∣i](ni)Fi\sum_{i=0}^n[k\mid i]\binom niF_ii=0∑n[k∣i](in)Fi
显然FFF可以通过矩阵表示出来
设
A=(1110),S=(10)A=\left(\begin{matrix}1&1\\1&0\end{matrix}\right),S=\left(\begin{matrix}1\\0\end{matrix}\right)A=(1110),S=(10)
那么只需要求出
∑i=0n[k∣i](ni)AiS\sum_{i=0}^n[k\mid i]\binom niA^iSi=0∑n[k∣i](in)AiS
相当于求
∑i=0n[k∣i](ni)Ai\sum_{i=0}^n[k\mid i]\binom niA^ii=0∑n[k∣i](in)Ai
然后日常把这个幻视成二项式定理害得我调了两个小时
显然可以单位根反演
1k∑j=0k−1∑i=0n(ni)Aiωij\frac 1k\sum_{j=0}^{k-1}\sum_{i=0}^n\binom niA^i\omega^{ij}k1j=0∑k−1i=0∑n(in)Aiωij
=1k∑j=0k−1∑i=0n(ni)(Aωj)i=\frac 1k\sum_{j=0}^{k-1}\sum_{i=0}^n\binom ni(A\omega^j)^i=k1j=0∑k−1i=0∑n(in)(Aωj)i
二项式定理走一波
=1k∑j=0k−1(Aωj+I)n=\frac 1k\sum_{j=0}^{k-1}(A\omega^j+I)^n=k1j=0∑k−1(Aωj+I)n
然后就可以算了
因为p%k=1p\%k=1p%k=1,所以可以找一个原根算出ω\omegaω
复杂度O(klogn)O(k\log n)O(klogn)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
using namespace std;
typedef long long ll;
ll n;
int k,MOD;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
inline int qpow(int a,int b)
{int ans=1;while (b){if (b&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;b>>=1;}return ans;
}
int g,Wn;
inline bool check(int x)
{int n=x;for (int i=2;i*i<=x;i++)if (x%i==0){if (qpow(g,n/i)==1) return false;while (x%i==0) x/=i;}if (x>1&&qpow(g,n/x)==1) return false;return true;
}
struct mat
{int e[2][2];mat(const int& x=0){e[0][0]=e[1][1]=x;e[0][1]=e[1][0]=0;}int* operator [](int i){return e[i];}
};
inline int det(mat m){return dec((ll)m[0][0]*m[1][1]%MOD,(ll)m[0][1]*m[1][0]%MOD);}
inline mat operator *(mat a,mat b)
{mat ans;ans[0][0]=add((ll)a[0][0]*b[0][0]%MOD,(ll)a[0][1]*b[1][0]%MOD);ans[0][1]=add((ll)a[0][0]*b[0][1]%MOD,(ll)a[0][1]*b[1][1]%MOD);ans[1][0]=add((ll)a[1][0]*b[0][0]%MOD,(ll)a[1][1]*b[1][0]%MOD);ans[1][1]=add((ll)a[1][0]*b[0][1]%MOD,(ll)a[1][1]*b[1][1]%MOD); return ans;
}
inline mat operator +(mat a,mat b)
{mat ans;ans[0][0]=add(a[0][0],b[0][0]);ans[0][1]=add(a[0][1],b[0][1]);ans[1][0]=add(a[1][0],b[1][0]);ans[1][1]=add(a[1][1],b[1][1]); return ans;
}
inline mat operator *(mat a,const int& t)
{mat ans;ans[0][0]=(ll)a[0][0]*t%MOD;ans[0][1]=(ll)a[0][1]*t%MOD;ans[1][0]=(ll)a[1][0]*t%MOD;ans[1][1]=(ll)a[1][1]*t%MOD; return ans;
}
inline mat qpow(mat a,ll p)
{mat ans(1);while (p){if (p&1) ans=ans*a;a=a*a;p>>=1;}return ans;
}
int main()
{int T;scanf("%d",&T);while (T--){scanf("%lld%d%d",&n,&k,&MOD);for(g=2;!check(MOD-1);g++);Wn=qpow(g,(MOD-1)/k);mat T,sum;T[0][0]=T[0][1]=T[1][0]=1;T[1][1]=0;for (int i=0;i<k;i++,T=T*Wn) sum=sum+qpow(T+mat(1),n);sum=sum*qpow(k,MOD-2);printf("%d\n",sum[0][0]);}return 0;
}