Codeforces 226C (140C) Anniversary

問題

n番目のフィボナッチ数をFiとする。
与えられた数l, r, kに対して、
Fl, Fl+1, ..., Frから異なるk個の数を選び、その最大公約数を求める。
全ての選び方のうち、最大公約数が最大になるようなときの、最大公約数をmod mで求めよ。

制約条件

l, r≦10^12
2≦k≦r - l + 1

方針

GCD(Fi, Fj) = F[GCD(i, j)]という関係を使えば、
l, l+1, ..., rからk個の数を選んで、GCMを最大化するという問題を解けばよいことがわかる。


GCMをqとしたとき、
l, rの間に含まれるqの倍数の個数は、r/q + (l-1)/q(割り算は切り捨て)
これがk以上ならよくて、k以上のうち最大になるqを見つけたい。


r/q + (l-1)/qの値が変化するqのところだけ見ればよい。
a/qがどこで値が変化するかというと、
√aまでは1ごとに値が変化して、それより大きいときは、
a/p = qとなるpごとに値が変化する。


なので、この値の変わり目は2 * √a個くらいしかない
これをl-1についても調べて、その変わり目上だけを実際に確かめればよい。


あとはn番目のフィボナッチ数を求めるだけだが、これは行列の累乗でlogの時間で求められる。

ソースコード

ll mod, l, r, k;

typedef vector<vi> M;
inline M operator*(const M & a, const M &b){
	M c(a.size(), vi(b[0].size()));
	rep(i, c.size()) rep(j, c[0].size()){
		ll s = 0;
		rep(k, a[0].size()) s += (ll)a[i][k] * b[k][j] % mod;
		c[i][j] = s % mod;
	}
	return c;
}
inline M pow(M a, ll m){
	M res(a.size(), vi(a.size()));
	rep(i, a.size()) res[i][i] = 1;
	for(; m; m /= 2){
		if(m & 1) res = res * a;
		a = a * a;
	}
	return res;
}
ll fib(ll n){
	M mat(2, vi(2, 1));
	mat[1][1] = 0;
	mat = pow(mat, n);
	return mat[1][0];
}
int main(){
	cin >> mod >> l >> r >> k;
	
	ll a = 1;
	vi cand;
	for(ll i = 1; i * i <= r; i++){
		cand.pb(i);
		cand.pb(r / i);
	}
	for(ll i = 1; i * i <= l - 1; i++){
		cand.pb((l - 1) / i);
	}
	rep(i, cand.size()) if(r / cand[i] - (l - 1) / cand[i] >= k)
		a = max(a, cand[i]);
	
	cout << fib(a) << endl;
	return 0;
}