TopCoder SRM 369 Div1 Medium AbsSequence

問題

次のように定義される数列aがある。
a[0] = first
a[1] = second
a[i] = | a[i - 1] - a[i - 2] | (i > 1)
このとき、aのx番目の要素を求めよ。

制約条件

first, second≦10^18
x≦10^18

方針

aを実際に最初のいくつかの項を出力してみると、
長さ3の周期で規則的になっていることがわかる。


a[i] = a[i - 3]で、
a[i - 1] = a[i - 4] - d
みたいになっていて、a[i - 1]が0未満になるところで、
数字だけ変わってまた同じ規則が現れる。


なので、a[i] = a[i - 3]が出てきたところで、
適当に繰り返し部分を圧縮してやる。
すると、gcdの計算みたいになって、O(log max{x, first, second})くらいの計算量でa[x]が求まる。

ソースコード

map<ll, ll> m;
ll solve(ll x){
	
	for(ll i = 2; i <= x; i++){
		ll nxt = abs(m[i - 1] - m[i - 2]);
		if(i < 4 || !m.count(i - 3) || m[i - 3] != nxt){
			m[i] = nxt;
			continue;
		}
		ll d = m[i - 4] - m[i - 1];
		ll loop = (x - i) / 3;
		if(d < 0) loop = 0;
		else if(d) loop = min(loop, m[i - 1] / d);
		
		m[i + loop * 3] = nxt;
		m[i + loop * 3 - 1] = m[i - 1] - d * loop;
		i += loop * 3;
	}
	return m[x];
}

class AbsSequence {
	public:
	vector <string> getElements(string first, string second, vector <string> indices) 
	{
		m.clear();
		m[0] = atoll(first.c_str());
		m[1] = atoll(second.c_str());
		
		vector<string> ans;
		rep(i, indices.size()){
			char buf[100];
			sprintf(buf, "%lld", solve(atoll(indices[i].c_str())));
			ans.pb(buf);
		}
		return ans;
	}
};