bzoj 3328 : PYXFIB
Discription
Input第一行一个正整数,表示数据组数据 ,接下来T行
每行三个正整数N,K,P
T行,每行输出一个整数,表示结果
Sample Input1
1 2 3
Sample Output
1
Hint
上个周学的一个 dark技巧: 利用单位根来 求一个多项式某个数倍数次数的项的系数和。
首先设 w[k] 为 复数域下的k次单位根,那么它满足的一个性质是: 当且仅当 k|i 的时候 ,w[k]^i = 1;其他时候都不为1。
这个有什么用处呢???
假设我们要求 杨辉三角的第n行的 是3的倍数的列的和的时候, 我们显然可以列出式子:ANS = ∑ [k|i] * C(n,i).
然后我们把 w[0]~w[k-1] 分别带进 (1+x)^n 这个多项式里,展开之后求和,显然 x^i前的系数是 C(n,i) ,并且 对于 是k的倍数的i ,w[0]^i + w[1]^i +....+w[k-1]^i = k;
而对于不是k倍数的i,我们用等比数列求一下和发现系数正好就是0。所以我们把这k个多项式的和再除以k就是要求的东西了。
对于本题其实是一样的,只不过我们把数的多项式换成矩阵的多项式就好啦。而且因为 p mod k =1,所以直接找原根然后就可以直接求k次单位根啦。
(无意间达成了一个成就2333 )
/* 利用单位根的性质,可以求出多项式某个数倍数的次数项的系数和 (I + xA)^n = ∑C(n,i) * x^i * A^i ,其中A是斐波那契数列系数矩阵 */ #include<bits/stdc++.h> #define ll long long using namespace std; int K,P,T,root,num,d[233],ans; inline int add(int x,int y){ x+=y; return x>=P?x-P:x;} inline int mul(int x,int y,const int ha){ return x*(ll)y%ha;} inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=mul(x,x,P)) if(y&1) an=mul(an,x,P); return an;} struct node{ int a[2][2]; inline void clear(){ memset(a,0,sizeof(a));} inline void BASE(){ clear(); a[0][0]=a[1][1]=1;} node operator *(const int &u)const{ node r; r.a[0][0]=mul(a[0][0],u,P); r.a[0][1]=mul(a[0][1],u,P); r.a[1][0]=mul(a[1][0],u,P); r.a[1][1]=mul(a[1][1],u,P); return r; } node operator +(const node &u)const{ node r; r.a[0][0]=add(a[0][0],u.a[0][0]); r.a[0][1]=add(a[0][1],u.a[0][1]); r.a[1][0]=add(a[1][0],u.a[1][0]); r.a[1][1]=add(a[1][1],u.a[1][1]); return r; } node operator *(const node &u)const{ node r; r.a[0][0]=add(mul(a[0][0],u.a[0][0],P),mul(a[0][1],u.a[1][0],P)); r.a[0][1]=add(mul(a[0][1],u.a[1][1],P),mul(a[0][0],u.a[0][1],P)); r.a[1][0]=add(mul(a[1][0],u.a[0][0],P),mul(a[1][1],u.a[1][0],P)); r.a[1][1]=add(mul(a[1][1],u.a[1][1],P),mul(a[1][0],u.a[0][1],P)); return r; } }X,ANS; inline void GD(){ int Q=P-1; num=0; for(int i=2;i*(ll)i<=Q;i++) if(!(Q%i)){ d[++num]=i; while(!(Q%i)) Q/=i; if(Q==1) break; } if(Q!=1) d[++num]=Q; for(int i=1;i<=num;i++) d[i]=(P-1)/d[i]; } inline void findroot(){ GD(); for(int i=2;i;i++){ bool flag=1; for(int j=1;j<=num;j++) if(ksm(i,d[j])==1){ flag=0; break;} if(flag){ root=i; break;} } } inline void calc(ll C){ for(int i=0,omega=ksm(root,(P-1)/K),now=1;i<K;i++,now=mul(now,omega,P)){ X.a[0][1]=X.a[1][1]=X.a[1][0]=1,X.a[0][0]=0; X=X*now,X.a[0][0]=add(X.a[0][0],1),X.a[1][1]=add(X.a[1][1],1); ll O=C; ANS.BASE(); for(;O;O>>=1,X=X*X) if(O&1) ANS=ANS*X; ans=add(ans,add(ANS.a[0][0],ANS.a[1][0])); } ans=mul(ans,ksm(K,P-2),P); } inline void solve(){ ll N; ans=0; scanf("%lld%d%d",&N,&K,&P); findroot(); calc(N); printf("%d\n",ans); } int main(){ scanf("%d",&T); while(T--) solve(); return 0; }
我爱学习,学习使我快乐