[学习笔记] bzoj 3328 PYXFIB - 单位根反演

64 篇文章 0 订阅
15 篇文章 0 订阅

首先求出一个原根g,那么单位根w取 g p − 1 k g^{\frac{p-1}k} gkp1
性质是这样的: w n w^n wn 0 0 0次到 k − 1 k-1 k1次相加取平均数为 1 1 1,当且仅当 k ∣ n k|n kn
构造多项式 F ( x ) = ( I + x M ) n F(x)=(I+xM)^n F(x)=(I+xM)n,I是单位矩阵,M是Fib数列的转移矩阵,那么其k次项系数显然就是 ( n k ) \binom n k (kn)。这样做完了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define lint long long
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int p,pri[100];
inline int fast_pow(int x,int k,int ans=1)
{	for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans;	}
inline int get_yg(int p)
{
	int x=p-1,s=sqrt(x+0.5),c=0;
	for(int i=2;i<=s;i++)
		if(x%i==0)
		{
			pri[++c]=i,x/=i;
			while(x%i==0) x/=i;
		}
	if(x>1) pri[++c]=x;int g=2;
	while(g<p-1)
	{
		int can_be_yg=1;
		for(int i=1;i<=c;i++)
			if(fast_pow(g,(p-1)/pri[i])==1)
			{	can_be_yg=0;break;	}
		if(can_be_yg) return g;g++;
	}
	return p-1;
}
inline int tms(int (*a)[3],int (*b)[3],int (*c)[3])
{
	static int w[3][3];
	for(int i=1;i<=2;i++)
		for(int j=1;j<=2;j++) w[i][j]=0;
	for(int i=1;i<=2;i++)
		for(int k=1;k<=2;k++)
			for(int j=1;j<=2;j++)
				w[i][j]+=(lint)a[i][k]*b[k][j]%p;
	for(int i=1;i<=2;i++)
		for(int j=1;j<=2;j++) c[i][j]=w[i][j]%p;
	return 0;
}
inline int fast_pow(int (*a)[3],int (*b)[3],lint k)
{
	for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) b[i][j]=(i==j);
	for(;k;k>>=1ll,tms(a,a,a)) (k&1ll)?tms(a,b,b):0;return b[1][1];
}
inline int calc(int w,lint n)
{
	static int a[3][3],b[3][3];
	for(int i=1;i<=2;i++)
		for(int j=1;j<=2;j++)
			a[i][j]=(i==j)+w*(i!=2||j!=2);
	return fast_pow(a,b,n),b[1][1];
}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		lint n;int k;scanf("%lld%d%d",&n,&k,&p);
		int g=get_yg(p),wn=fast_pow(g,(p-1)/k),ans=0;
		for(int i=0,w=1;i<k;i++,w=(lint)w*wn%p) (ans+=calc(w,n))%=p;
		printf("%lld\n",(lint)ans*fast_pow(k,p-2)%p);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值