PKU 3092 Non-divisible 2-3 Power Sums

問題

与えられた数nを、
n=Σ2^ni 3^miの形の和で表せ。
ただし、任意の互いに異なるi, jについて2^ni 3^miは2^nj 3^mjを割り切ってはならない。

制約条件

n<2^31

方針

和の項を3の指数が小さい順に並べる。
ここで、2の指数が大きい順に並んでいないといけない。


3^m0で全体をくくると、
n = 3^m0 Σ2^ni 3^(mi-m0)となり、同じ問題で、サイズが小さいものに還元される。
したがって、次のようなメモ化再帰によって解けばいい。


rec(n, m) := 2の指数をm以下にして、nという数を和の形で表せるかどうか。
nは大きいが、m≦31で、nも指数的に減少するので、状態数はあまり多くない。

ソースコード

map<pi, pi> dp;
pi rec(int n, int two){
	if(dp.count(mp(n, two))) return dp[mp(n, two)];
	pi &res = dp[mp(n, two)];
	
	if(n == 0) return res = mp(0, 0);
	if(two == 0) return res = mp(-1, 0); // fail;
	
	rep(i, two){
		int nxt = n - (1 << i);
		if(nxt < 0 || nxt % 3) continue;
		while(nxt % 3 == 0){
			nxt /= 3;
			pi tmp = rec(nxt, i);
			if(tmp.first >= 0) return res = mp(nxt, i);
		}
	}
	return res = mp(-1, 0);
	
}

int main()
{
	int CS;
	cin >> CS;
	rep(cs, CS){
		int n;
		cin >> n;
		
		int three = 0;
		while(n % 3 == 0) n /= 3, three++;
		pi cur = mp(n, 31);
		vector<pi> ans;
		
		while(1){
			pi next = rec(cur.first, cur.second);
			int d = cur.first - (1 << next.second);
			int nt = three;
			while(d > next.first) d /= 3, nt++;
			ans.pb(mp(next.second, three));
			cur = next;
			three = nt;
			if(cur.first == 0) break;
		}
		
		printf("%d %d", cs + 1, ans.size());
		rep(i, ans.size()){
			printf(" [%d,%d]", ans[i].first, ans[i].second);
		}
		puts("");
	}
	return 0;
}