链接:https://nanti.jisuanke.com/t/41355
题意:
Q个询问,每次求F(N),但是N要用上一次询问的结果得到。
思路:
1、直接矩阵快速幂求,再用map记一下答案,求过就不求了。数据正常的话肯定就会T,但这题数据太水。(也可能是询问加密的问题,反正理论上铁定T。)
#include
#include
#include
#include
#include
思路2(正解):首先,这是一个广义斐波那契数列,也是一个常系数线性递推数列。常系数线性递推数列的通项公式,有很多方法,我参考了百度求斐波那契数列通项公式的步骤,总结了一下求广义斐波那契数列通项公式的套路。
常系数线性递推数列的通项公式的一般形式为:
A(n)=d1*x1^n+d2*x2^n+...+dk*xk^n
现有广义斐波那契数列
联立得:
广义斐波那契数列通项公式的一般形式为:F(n)=k1*r^n+k2*s^n
将数列的前两项代入即可解出k1,k2。套路结束。
对于本题:
C1=3,C2=2,F(1)=1,F(2)=3。通项公式便为:F(n)=1/sqrt(17)*[((3+sqrt(17))/2)^n-((3-sqrt(17))/2)^n]。
得到公式我们看到有根号,这可咋办,再怎么高精度也不行啊。这里就要用到二次剩余(就是求一个数开根号后对模数取模的等价数。)(至于怎么求,鑫爷会。嘿嘿。)例如本题,就是要求sqrt(17)在模998244353时的等价数,也就是17的二次剩余。假设二次剩余为x,那么公式里的全部sqrt(17)都可以换为x。这样我们可以用快速幂logn的求一次询问,这样求复杂度和矩阵快速幂是同一量级的只不过常数小一些。
对于一个数x,求(x^n)%p,显然可以用欧拉定理降幂为x^(n%phi(p)+phi(p)),本题p=998244353,那么指数n最大便为:1996488703。我们可以分块预处理,设N=sqrt(1996488703),把X[0]=x^0,X[1]=x^1,...,X[n]=x^N预处理出来,再把pre[0]=x^(0*N),pre[1]=x^(1*N),...,pre[N]=x^(N*N))。
求出来,这样对于一个询问n=a*N+b,我们其实就是求 (x^(a*N))*(x^b),即pre[a]*X[b]=pre[n/N]*X[n%N]。
#include
#define ll long long
using namespace std;
const int N = 5e4;
const ll mod = 998244353;
const ll inv17 = 438914993;//sqrt(17)的逆元
const ll x1 = 736044383;//(3+sqrt(17))/2
const ll x2 = 262199973;//(3-sqrt(17))/2
ll X1[N+10],X2[N+10],pre1[N+10],pre2[N+10];
ll ans,n,q,nn,res;
ll mo(ll x){ return x>=mod?x%mod:x; }
void Init()
{
pre1[0]=pre2[0]=X1[0]=X2[0]=1;
for(int i=1;i<=N;i++)
X1[i]=mo(X1[i-1]*x1),X2[i]=mo(X2[i-1]*x2);
for(int i=1;i<=N;i++)
pre1[i]=mo(pre1[i-1]*X1[N]),pre2[i]=mo(pre2[i-1]*X2[N]);
}
int main(void)
{
Init();
scanf("%lld%lld",&q,&n);
res=ans=0;
while(q--)
{
n^=(res*res);
nn=n%(mod-1)+mod-1;
res=mo(pre1[nn/N]*X1[nn%N])-mo(pre2[nn/N]*X2[nn%N]);
res=mo(res+mod);
res=mo(res*inv17);
ans^=res;
}
printf("%lld\n",ans);
return 0;
}