No.25 有限小数

No.25 有限小数 - yukicoder

  • 高校時代を思い出した。こんな感じの問題解いた記憶があった。
  • コメントのとおり overflow してしまった。くそー。
class FiniteDecimal {
public:
    void solve(void) {
            ll N,M;
            cin>>N>>M;
            ll g = gcd(N,M);
            // 互いに素にしておく
            N /= g;
            M /= g;

            if (N % M == 0)
            {
                ll x = N/M;
                while (x%10 == 0)
                    x /= 10;
                cout<<x%10<<endl;
                return;
            }
            // 有限少数なら
            //           K
            // N/M = A + ∑ a[k] * 10^(-k) (A は整数) と書ける
            //          k=1
            //
            // 式変形すると
            // 10^K * N = M * (10*A'+a[K]) = M * B
            //
            // N,M は互いに素なので M*B が 10 の冪乗であることが必要
            // B は 10 の倍数でないので M は 5 or 2 の冪で割りきれる
            //
            // 逆に M が 5 or 2 で割り切れるなら
            //
            // N = ∑ c[k] * 10^k と書けて N/M の各項は
            //
            //  c[k]*10^k
            // -----------  となる
            //  5^k * 2^l
            //
            // 分子分母に適当に数字w書けて b/10^m の形にできる。
            //
            int k1 = 0;
            ll  m = M;
            while (m%5 == 0)
            {
                m /= 5;
                ++k1;
            }
            int k2 = 0;
            while (m%2 == 0)
            {
                m /= 2;
                ++k2;
            }
            // M = c * 5^k1 * 2^k2
            if (m == 1)
            {
                // (ll)(N*pow(10,K)/M) % 10 だと overflow するので
                // 10^K/M のかわりに 2^k1 * 5^k2 をかける

                // 関係があるのは末尾桁のうち 0 じゃないものだけなので
                // overflow 対策にけずっておく
                while (N % 10 == 0) N /= 10;
                N %= 10;
                while (k1 > 0)
                {
                    N *= 2;
                    while (N % 10 == 0) N /= 10;
                    N %= 10;
                    --k1;
                }
                while (k2 > 0)
                {
                    N *= 5;
                    while (N % 10 == 0) N /= 10;
                    N %= 10;
                    --k2;
                }
                while (N % 10 == 0) N /= 10;
                cout<<N%10<<endl;
            }
            else  // 割り切れなかった(有限小数ではない)
                cout<<-1<<endl;
    }
};