The 2019 Asia Nanchang First Round Online Programming Contest (H The Nth Item)
题意
给定一个递归式,给定一个数字n和q次操作,每次操作将上一次的答案ans和上一次的n,(ans*ans)^n作为这次的n,并将每次的ans异或起来作为最终答案输出。
思路
根据广义斐波那契数列,我们可以得到此递归式的通项公式:
sqrt(17)我们可以用二次剩余求出它的值。
此时,我们可以O(log(n))的求出每一次查询的值。
显然,出题人并不愿意就这样放过我们,所以我们得找n次幂的那两坨东西的幂次的循环节,这样就能把1e18的n降下来了。
然鹅出题人。。。我们必须要O(1)的知道某一项的答案。
记n次幂中间那一坨东西为x,
易知:
用程序跑出来我们分别知道了那两坨东西的幂次的循环节,记为xhj(循环节),所以,n<xhj,所以,我们只需要分别预处理出来1~ xhj/100次幂的x100和1~ 100次幂的x即可O(1)的访问答案。
终于可以了。。。
据说强制在线写的有点小bug,用杜教BM+记忆化也能过。。。
坑点
哪儿不是坑点。
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
ll quick_pow(ll a,ll b,ll p)
{
ll ans=1;
a%=p;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
const int N=1e7+5;
const ll MOD=998244353;
const ll sqr17=524399943;
const ll inv2=quick_pow(2,MOD-2,MOD);
const ll num1=(3+sqr17)*inv2%MOD;
const ll num2=(3-sqr17+MOD)*inv2%MOD;
const ll mod1=249561088;
const ll mod2=29360128;
const ll inv17=quick_pow(17,MOD-2,MOD);
const ll invsqr17=quick_pow(sqr17,MOD-2,MOD);
ll arr1[105];
ll arr2[105];
ll ar1[mod1/100+5];
ll ar2[mod2/100+5];
void init()
{
arr1[0]=1;
arr2[0]=1;
rep(i,1,100)
{
arr1[i]=arr1[i-1]*num1%MOD;
arr2[i]=arr2[i-1]*num2%MOD;
}
ar1[0]=1;
ar2[0]=1;
rep(i,1,mod1/100+2)
{
ar1[i]=ar1[i-1]*arr1[100]%MOD;
}
rep(i,1,mod2/100+2)
{
ar2[i]=ar2[i-1]*arr2[100]%MOD;
}
}
ll fun(ll n)
{
if(n==0)return 0;
if(n==1)return 1;
ll n1=n%mod1;
ll n2=n%mod2;
return ((ar1[n1/100]*arr1[n1%100]%MOD-ar2[n2/100]*arr2[n2%100]%MOD+MOD)%MOD)*invsqr17%MOD;
}
int main()
{
init();
int q;
ll n;
scanf("%d%lld",&q,&n);
ll ans=0;
ll answer=0;
while(q--)
{
n=(ans*ans)^n;
ans=fun(n);
answer^=ans;
}
printf("%lld\n",answer);
return 0;
}