Official

G - べき表現/Power Expression Editorial by penguinman


解説をするにあたり、数列 \(a\) の要素全てを \(x\) 倍する操作を \(a\cdot x\) と、\(2\) 数列 \(a\), \(b\) を連結する操作を \(a+b\) と表すことにします。

\(N=x\) の場合に問題文中の条件を満たす数列を返す関数を \(f(x)\) と定義します。このような \(f(x)\) は以下のように再帰的に構築することができます。

\[ f(x)=\begin{cases} \emptyset\ (x=0)\\ f(\frac{x}{3})\cdot 3\ (x \neq 0,\ x\equiv 0\bmod\ 3)\\ (f(\frac{x-1}{3})\cdot 3)+(1)\ (x\equiv 1\bmod\ 3)\\ (f(\frac{x+1}{3})\cdot 3)+(-1)\ (x\equiv 2\bmod\ 3) \end{cases} \]

これをこのままコードに移し、\(f(N)\) を求めれば良いです。計算量は \(O(\log^2N)\) です。

解答例 (Python)

N = int(input())
def f(x):
    if x == 0:
        return []
    ret = []
    if x%3 == 0:
        ret = [i*3 for i in f(x//3)]
    if x%3 == 1:
        ret = [i*3 for i in f((x-1)//3)]
        ret.append(1)
    elif x%3 == 2:
        ret = [i*3 for i in f((x+1)//3)]
        ret.append(-1)
    return ret
ans = f(N)
print(len(ans))
print(' '.join(map(str,ans)))

なお、非再帰で書くとより簡潔です。この実装の計算量は \(O(\log N)\) です。

解答例 (C++)

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main(){
    ll N; cin >> N;
    ll now = 1;
    vector<ll> ans;
    while(N){
        if(N%3 == 1){
            ans.push_back(now);
            N--;
        }
        else if(N%3 == 2){
            ans.push_back(-now);
            N++;
        }
        now *= 3;
        N /= 3;
    }
    cout << ans.size() << endl;
    for(auto p:ans) cout << p << " ";
    cout << endl;
}

これは余談ですが、答えとなる数列 \(A\) は並び替えを除き一意に定まります。詳しくは「平衡三進法」と検索してみてください。

posted:
last update: