bzoj 3328 : PYXFIB

Discription

Input

第一行一个正整数,表示数据组数据 ,接下来T行
每行三个正整数N,K,P

Output

T行,每行输出一个整数,表示结果

Sample Input
1
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;
}

  

posted @ 2018-04-19 11:42  蒟蒻JHY  阅读(311)  评论(0编辑  收藏  举报