分块打表 H. The Nth Item @ The 2019 Asia Nanchang First Round Online Programming Contest

分块打表 H. The Nth Item @ The 2019 Asia Nanchang First Round Online Programming Contest

题意

1e7次询问f(n)%998244353: n<1e18
f ( 0 ) = 0 , f ( 1 ) = 1 ; f ( i ) = 3 f ( i − 1 ) + 2 f ( i − 2 ) f(0)=0,f(1)=1;\\ f(i)=3f(i-1)+2f(i-2)\\ f(0)=0,f(1)=1;f(i)=3f(i1)+2f(i2)

题解

题目要求O(1)地求二阶线性递推数列的第n项。矩阵快速幂可以 O ( 4 l o g n ) O(4logn) O(4logn)求,其中4为矩阵乘法常数。当然也可以选择求解递推式,得到:
f ( n ) = ( ( ( 3 + 17 ) 2 ) n − ( ( 3 − 17 ) 2 ) n ) / 17 f(n)=((\frac{(3+\sqrt {17})}{2})^n-(\frac{(3-\sqrt {17})}{2})^n)/\sqrt{17} f(n)=((2(3+17 ))n(2(317 ))n)/17
利用二次剩余得到 17 M O D 998244353 \sqrt {17}MOD998244353 17 MOD998244353,快速幂的复杂度为 O ( 2 l o g n ) O(2logn) O(2logn)

问题是如何降到O(1)?

首先尝试找循环节,如果循环节<1e7即可预处理出所有取值,然而循环节为$ 499122176$ 无法直接预处理。

考虑矩阵转移式:
( f ( n + 1 ) f ( n ) ) = ( 3 2 1 0 ) n ⋅ ( 1 0 ) \begin{pmatrix} f(n+1) \\ f(n) \end{pmatrix} =\begin{pmatrix} 3 &amp; 2\\ 1 &amp; 0 \end{pmatrix}^n \cdot \begin{pmatrix} 1 \\ 0 \end{pmatrix} (f(n+1)f(n))=(3120)n(10)

T = ( 3 2 1 0 ) T=\begin{pmatrix} 3 &amp; 2\\ 1 &amp; 0 \end{pmatrix} T=(3120)
于是原问题等价于O(1)查询 T n T^n Tn

考虑分块,每隔i个数算一次T并存下来,有:
T i , T 2 i , T 3 i . . . T^i,T^{2i},T^{3i}... Ti,T2i,T3i...
对于某次查询 T n T^n Tn,必定落在
[ T ⌊ n i ⌋ i , T ⌊ n i ⌋ i + i ) [T^{\lfloor \frac{n}{i}\rfloor i},T^{\lfloor \frac{n}{i}\rfloor i+i}) [Tini,Tini+i)
为了求 T n T^n Tn还需乘上
T   n % i T^{\ n\%i} T n%i
这部分我们可以暴力算,当然也可以再打一个表存
T 1 , T 2 . . . T i − 1 T^1,T^2...T^{i-1} T1,T2...Ti1
打表预处理的时空复杂度为
T ( n ) = n / i + i T(n)=n/i+i T(n)=n/i+i
i = n i=\sqrt n i=n 时取到最小值 O ( 2 n ) O(2\sqrt n) O(2n ),当然这道题利用循环节,i 取100即可,600ms过。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<iostream>
#define ll long long 
using namespace std;
const int N = 2;
const int inf = 50010;
typedef long long LL;
const int MOD = 998244353;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
struct Matrix
{
	LL m[N][N];
};

Matrix A;
Matrix I = { 1, 0, 0, 1 };

Matrix multi(Matrix a, Matrix b)
{
	Matrix c;
	for (int i = 0; i<N; i++)
	{
		for (int j = 0; j<N; j++)
		{
			c.m[i][j] = 0;
			for (int k = 0; k<N; k++)
			{
				c.m[i][j] += a.m[i][k] * b.m[k][j];
				c.m[i][j] %= MOD;
			}
		}
	}
	return c;
}

Matrix power(Matrix A, LL n)
{
	Matrix ans = I, p = A;
	while (n)
	{
		if (n & 1)
		{
			ans = multi(ans, p);
			n--;
		}
		n >>= 1;
		p = multi(p, p);
	}
	return ans;
}
int L = 499122176;
Matrix as[101];
const int maxn = 5e6;
Matrix tab[maxn + 1];
ll f(int x) {
	Matrix ans = multi(tab[x/100],as[x%100]);
	return ans.m[1][0] ;
}

int main()
{
	as[0]={ 1, 0, 0, 1 };
	as[1].m[0][0] = 3;
	as[1].m[0][1] = 2;
	as[1].m[1][0] = 1;
	as[1].m[1][1] = 0;
	rep(i, 1, 99) {
		as[i + 1] = multi(as[i] ,as[1]);
	}
	tab[0] = { 1, 0, 0, 1 };
	rep(i, 1, maxn) {
		tab[i] = multi(tab[i - 1], as[100]);
	}
	ll q, n;
	cin >> q >> n;
	ll ans = 0;
	ll now = 0, last = n;
	rep(i, 1, q) {
		now = f((last-1)%L+1);
		ans ^= now;
		last = (now*now)^last;
	}
	cout << ans << endl;
	cin >> n;
	return 0;
}
/*
1 1
499122176
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值