P5110-块速递推【特征方程,分块】

正题

题目链接:https://www.luogu.com.cn/problem/P5110


题目大意

数列\(a\)满足

\[a_n=233a_{n-1}+666a_{n-2},a_0=0,a_1=1 \]

\(T\)组询问给出\(n\)\(a_n\)

\(1\leq T\leq 5\times 10^7\)\(n\)\(\text{unsigned long long}\)范围内


解题思路

上面那个递推式的特征方程就是\(x^2-233x-666\),直接带式子解出来\(x_0=\frac{233+\sqrt{56953}}{2},x_1=\frac{233-\sqrt{56953}}{2}\)

然后设\(a_n=c_0x_0^n+c_1x_1^n\),那么带入\(a_0\)\(a_1\)就有

\[\left\{\begin{matrix}c_0+c_1=0\\c_0\frac{233+\sqrt{56953}}{2}+c_1\frac{233-\sqrt{56953}}{2}=1\end{matrix}\right. \]

解出来有\(c_0=\frac{1}{\sqrt{56953}},c_1=-\frac{1}{\sqrt{56953}}\)

这样我们就可以\(O(T\log n)\)求答案了,但是还是不够。

先根据欧拉定理让\(n\)模上\(\varphi(P)\)缩小范围

然后分块处理快速幂,处理出\(x^i\)\(x^{i\sqrt P}(i\in[0,\sqrt P])\),这个是\(O(\sqrt P)\)的,然后每次把\(n\)分为整块的成上末尾的就好了。

时间复杂度\(O(\sqrt P+T)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const ll P=1e9+7,Phi=P-1;
const ll sq=188305837,T=32000;
ll Q,n,p0[T+1],p1[T+1],P0[T+1],P1[T+1],ans;
ll pw0(ll x){return P0[x/T]*p0[x%T]%P;}
ll pw1(ll x){return P1[x/T]*p1[x%T]%P;}
namespace Mker
{
	unsigned long long SA,SB,SC;
	void init(){scanf("%llu%llu%llu",&SA,&SB,&SC);}
	unsigned long long rand()
	{
	    SA^=SA<<32,SA^=SA>>13,SA^=SA<<1;
	    unsigned long long t=SA;
		SA=SB,SB=SC,SC^=t^SA;return SC%Phi;
	}
}
signed main()
{
	ll inv2=(P+1)/2;
	ll x0=(233+sq)*inv2%P,x1=(P+233-sq)*inv2%P;
	p0[0]=p1[0]=P0[0]=P1[0]=1;
	for(ll i=1;i<=T;i++)
		p0[i]=p0[i-1]*x0%P,p1[i]=p1[i-1]*x1%P;
	for(ll i=1;i<=T;i++)
		P0[i]=P0[i-1]*p0[T]%P,P1[i]=P1[i-1]*p1[T]%P;
	ll inv=233230706,c0=inv,c1=P-inv;
	scanf("%lld",&Q);Mker::init();
	while(Q--){
		n=Mker::rand();
//		scanf("%lld",&n);ans=0;
		ans^=(c0*pw0(n)+c1*pw1(n))%P;
//		printf("%lld\n",ans);
	}
	printf("%lld\n",ans);
}
posted @ 2021-02-24 11:27  QuantAsk  阅读(49)  评论(0编辑  收藏  举报