SRM 546 250pt : KleofasTail
問題概要
X[n]は次のように定義される無限の長さの数列である。
- X[n][0] = n
- X[n][i+1] = X[n][i] is even ? X[n][i] / 2 : X[n][i] - 1
数Kが与えられる。Kを含むようなnで、A<=n<=Bであるようなものの個数を求める問題。数値は10^18以下。
考えたこととか
- こういうのは逆から考えるのが典型。
- Kが偶数の時Kの前はK+1か2*K、奇数の時は前は2*K。
- えっと、これは計算量フィボナッチというアレ…?いやいや、時間全然足りない。
- そもそも、計算量はフィボナッチにはならない。
- すぐに解法見えないので、とりあえず手を動かして計算しよう…。
- 全然見えない…。
- とりあえず最初の項をaと表記すると…、aから辿っていったときすべての状態は奇数*2^kと表すことができて、そんな感じで行けるかなー?2倍と+1でなんかこうすべての整数が書けそうだし…。
- 時間がかなり経っていたので書き始める。合わない。そもそもあまり自身の無い解法だし諦めて500行こう…。
解法
Kに対して、2*Kと2*K+1は必ず親であることを利用する。したがって、区間を[K,K+1)からスタートして次の区間を[L[i+1], R[i+1]) = [2*L[i], 2*R[i])と得ることができるので、後はこれと[A, B)の共通部分をとれば良い。
acceptされたコード
#include <algorithm> using namespace std; typedef long long int64; //return intersection [x1, x2), [y1, y2) int64 get_intersection(int64 x1, int64 x2, int64 y1, int64 y2){ return max<int64>(0, min(y1, y2) - max(x1, x2)); } struct KleofasTail { int64 countGoodSequences(int64 K, int64 A, int64 B) { ++B; if(K == 0){ return B - A; } int64 ans = 0; if(K&1){ ans += (A <= K && K < B) ? 1 : 0; K *= 2; } int64 left = K, right = K + 2; for(;left <= B;){ ans += get_intersection(A, left, B, right); left *= 2; right *= 2; } return ans; } };