NOSSの雑記

主に競プロでやったことを書きます。

ABC044 D - 桁和

問題

自然数 nb 進数で表したときの桁和を f(b,n) とするとき、f(b,n) = s を満たす最小の b を求めよ。

問題リンク:ABC044 D

制約

  • 1 \le n \le 10^{11}
  • 1 \le s \le 10^{11}

解法

条件をまとめると、

  • n = a_{m}b^{m} + a_{m-1}b^{m-1} + ... + a_{1}b + a_0
  • s = a_{m} + a_{m-1} + ... + a_{1} + a_0

となるような  b を探せばよい。

ここで2つの式の差をとると、

n - s = a_{m}(b^{m}-1) + a_{m-1}(b^{m-1}-1) + ... + a_{1}(b-1)

となる。b^{k}-1 (k\ge1) は必ず b-1 を因数に持つことから右辺は b-1 で割り切れる。すなわち、

n-s \equiv 0 \mod b-1

が成り立つことが導かれる。よって解の候補は n-s の約数+1。

以上から、n-s の全ての約数 x 対し f(x+1,n) を求め s と一致するかを確認すれば解が求まる。n \le s の場合は b=n+1 以外の候補はない。

Code

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

ll f(ll b, ll n){
    if(n<b) return n;
    else return f(b,n/b)+n%b;
}

vector<ll> divisor(ll n) {
    vector<ll> res;
    for (ll i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            res.push_back(i);
            if (i != n / i) res.push_back(n / i);
        }
    }
    return res;
}

int main() {
    ll n, s;
    cin >> n >> s;
    vector<ll> d = divisor(n-s);
    d.push_back(n);
    sort(d.begin(), d.end());
    for(auto x : d){
        if(f(x+1,n)==s){
            cout << x+1 << endl;
            return 0;
        }
    }
    cout << -1 << endl;
    return 0;
}