题解:CF226C Anniversary

题目传送门

说明

F F F 为斐波那契数列。

性质

首先,你需要知道一个性质:
gcd ⁡ ( F n , F n + 1 ) = 1 \Large\gcd(F_n,F_{n+1})=1 gcd(Fn,Fn+1)=1
证明可以用辗转相减法,由于本人不会 Latex,那么直接用文字证明:

  • F n + 1 − F n = F n − 1 F_{n+1}-F{n}=F{n-1} Fn+1Fn=Fn1,所以:
  • gcd ⁡ ( F n , F n + 1 ) = gcd ⁡ ( F n − 1 , F n ) \gcd(F_n,F_{n+1})=\gcd(F_{n-1},F_n) gcd(Fn,Fn+1)=gcd(Fn1,Fn)
  • 那么,当 n n n 递归到 2 的时候就得 gcd ⁡ ( 1 , 1 ) = 1 \gcd(1,1)=1 gcd(1,1)=1
  • 固得证。

之后,我们要证明一个东西。

n < m n<m n<m x = F n x=F_n x=Fn y = F n + 1 y=F_{n+1} y=Fn+1,那么:

  • F n + 1 = x + y F_{n+1}=x+y Fn+1=x+y F n + 2 = x + 2 y F_{n+2}=x+2y Fn+2=x+2y F n + 3 = 2 x + 3 y F_{n+3}=2x+3y Fn+3=2x+3y…… F m = F m − n − 1 × x + F m − n × b F_{m}=F_{m-n-1}\times x+F_{m-n}\times b Fm=Fmn1×x+Fmn×b
  • 那么, F m = F m − n − 1 × F n + F m − n × F n + 1 F_m=F_{m-n-1}\times F_n+F_{m-n}\times F_{n+1} Fm=Fmn1×Fn+Fmn×Fn+1
  • 那么, gcd ⁡ ( F n , F m ) = gcd ⁡ ( F n , F m − n × F n + 1 ) \gcd(F_n,F_m)=\gcd(F_n,F_{m-n}\times F_{n+1}) gcd(Fn,Fm)=gcd(Fn,Fmn×Fn+1)
  • 由互质性质可知, gcd ⁡ ( F n , F m ) = gcd ⁡ ( F n , F m − n ) \gcd(F_n,F_m)=\gcd(F_n,F_{m-n}) gcd(Fn,Fm)=gcd(Fn,Fmn)
  • 递归下去,可以知道答案就是 F gcd ⁡ ( n , m ) F_{\gcd(n,m)} Fgcd(n,m)

分析

我们需要求出一个最大的 r e s res res,使得 ∑ i = l r [ r e s ∣ F i ] ≥ k \sum_{i=l}^{r}[res|F_i]\ge k i=lr[resFi]k。由于上述的性质,所以可以变为: ∑ i = 1 r [ r e s ∣ i ] ≥ k \sum_{i=1}^{r}[res|i]\ge k i=1r[resi]k。注意!这个 r e s res res 不可以二分。证明:

  • 设计数函数 c n t ( r e s ) = ⌊ r r e s ⌋ − ⌊ l − 1 r e s ⌋ cnt(res)=\lfloor\frac{r}{res}\rfloor-\lfloor\frac{l-1}{res}\rfloor cnt(res)=resrresl1。那么二分所需要的单调性是 c n t ( r e s ) ≥ c n t ( r e s + 1 ) cnt(res)\ge cnt(res+1) cnt(res)cnt(res+1)
  • 但是, l = 4 , r = 8 l=4,r=8 l=4,r=8 的情况可以知道 c n t ( 3 ) < c n t ( 4 ) cnt(3)<cnt(4) cnt(3)<cnt(4),原因是向下取整会有精度流失,不能够强行证明。而且如果两端都是 r e s + 1 res+1 res+1 的倍数,那么 c n t ( r e s + 1 ) ≥ 2 cnt(res+1)\ge2 cnt(res+1)2,但是只能保证 c n t ( r e s ) ≥ 1 cnt(res)\ge1 cnt(res)1

那么怎么样才能够求出这一个 r e s res res 呢?其实可以用根号枚举。我们需要的是 c n t ( r e s ) cnt(res) cnt(res) 不同的结果,这样的结果最多有 ⌊ r ⌋ \lfloor\sqrt{r}\rfloor r 个,有时候会多一些。那么,我们求:
r e s = max ⁡ i = 1 ⌊ r ⌋ ( max ⁡ ( c n t ( i ) , c n t ( ⌊ n i ⌋ ) ) ) \Large res=\max_{i=1}^{\lfloor\sqrt{r}\rfloor}(\max(cnt(i),cnt(\lfloor\frac{n}{i}\rfloor))) res=i=1maxr (max(cnt(i),cnt(⌊in⌋)))
即可。

加速

可以发现这一个值可能会很大,那么我们需要很高效的算法,求出 F r e s F_{res} Fres。这个算法就是矩阵快速幂优化 dp。

可以发现斐波那契数列本质上是一个 dp,即:
d p i = { 1 i ≤ 2 d p i − 2 + d p i − 1 i > 3 dp_i=\begin{cases} 1&i\le2\\ dp_{i-2}+dp_{i-1}&i>3 \end{cases} dpi={1dpi2+dpi1i2i>3
矩阵快速幂的本质是利用矩阵乘法的乘法结合律来用 log ⁡ n \log n logn 的时间复杂度来求出分矩阵,然后乘上原矩阵。

矩阵乘法的规则如下:

  • 定义 x x x 为长度 n , m n,m n,m 的矩阵, y y y 为长度 m , k m,k m,k 的矩阵,那么它们相乘的结果为长度为 n , k n,k n,k 的矩阵 r e s res res
  • r e s i , j = ∑ k = 1 m x i , k × y k , j res_{i,j}=\sum_{k=1}^{m}x_{i,k}\times y_{k,j} resi,j=k=1mxi,k×yk,j

可以看出这一个式子似乎能够优化所有转移方程只带加法和乘法的 dp。但是,要证明两点:

  • 矩阵乘法不满足乘法交换律,因为交换后 n n n 可能不等于 k k k
  • 矩阵乘法满足结合律。因为 { n , m } × { m , k } × { k , l } \{n,m\}\times\{m,k\}\times\{k,l\} {n,m}×{m,k}×{k,l},不管先算哪一个,要么得出 { n , k } × { k , l } = { n , l } \{n,k\}\times\{k,l\}=\{n,l\} {n,k}×{k,l}={n,l},要么得出 { n , m } × { m , l } = { n , l } \{n,m\}\times\{m,l\}=\{n,l\} {n,m}×{m,l}={n,l}

所以,矩阵乘法满足结合律。那么,矩阵乘法的递推式要视情况而定。比如这一道题目需要:

( 0 1 ) × ( 1 1 1 0 ) r e s \Large\begin{pmatrix} 0&1 \end{pmatrix}\times\Large\begin{pmatrix} 1&1\\ 1&0 \end{pmatrix}^{res} (01)× 1110 res
因为可以知道 r e s res res 等于 1 的时候,乘完之后会得:
( 1 1 ) \Large\begin{pmatrix} 1&1 \end{pmatrix} (11)
这时候可能不太明显,再乘 n n n 次:
( 1 2 ) \Large\begin{pmatrix} 1&2 \end{pmatrix} (12)
( 2 3 ) \Large\begin{pmatrix} 2&3 \end{pmatrix} (23)
( 3 5 ) \Large\begin{pmatrix} 3&5 \end{pmatrix} (35)
( 5 8 ) \Large\begin{pmatrix} 5&8 \end{pmatrix} (58)
这是斐波那契数列。为什么是斐波那契数列呢?因为可以变成:
( d p i − 1 d p i ) × ( 1 1 1 0 ) = ( d p i d p i − 1 + d p i ) \Large\begin{pmatrix} dp_{i-1}&dp_{i} \end{pmatrix}\times\Large\begin{pmatrix} 1&1\\ 1&0 \end{pmatrix}=\begin{pmatrix} dp_{i}&dp_{i-1}+dp_{i} \end{pmatrix} (dpi1dpi)× 1110 =(dpidpi1+dpi)
这就是矩阵快速幂优化 dp 的过程。在一些线性 dp 会超时的时候就可以用矩阵快速幂优化。可以优化到 log ⁡ n \log n logn 级别的递推式。

所以,可以构造如上的矩阵然后求矩阵快速幂。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct matrix{
	int n,m;
	ll a[3][3];
	matrix(){
		memset(a,0,sizeof(a));
	}
	inline ll *operator[](int index){
		return a[index];
	} 
};
int mod;
ll l,r,k;
inline matrix operator*(matrix x,matrix y){
	matrix res;
	res.n=x.n;
	res.m=y.m;
	for(int i=1;i<=x.n;++i){
		for(int j=1;j<=x.m;++j){
			for(int k=1;k<=y.m;++k){
				(res[i][k]+=x[i][j]*y[j][k])%=mod;
			}
		}
	}
	return res;
}
inline matrix power(matrix x,ll y){
	matrix res=x;
	while(y){
		if(y&1){
			res=res*x;
		}
		y>>=1;
		x=x*x;
	}
	return res;
}
int main(){
	scanf("%d %lld %lld %lld",&mod,&l,&r,&k);
	ll res=0;
	for(ll i=1;i*i<=r;++i){
		if(r/i-(l-1)/i>=k){
			res=max(res,i);
		}
		if(r/(r/i)-(l-1)/(r/i)>=k){
			res=max(res,r/i);
		}
	}
	matrix x,y;
	x.n=x.m=y.n=2;
	y.m=1;
	x[1][1]=x[1][2]=x[2][1]=y[2][1]=1;
	printf("%lld",matrix(power(x,res-1)*y)[1][1]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值