AtCoder Regular Contest 084 D - Small Multiple

http://arc084.contest.atcoder.jp/tasks/arc084_b

問題概要

正整数Kのすべての正の倍数において、10進法で表したときの各桁の和の最小値を求めよ。

2 <= K <= 105

解法

以下のようなグラフを考え、最短経路問題を解く。

  • 頂点は0~(K-1)のK個
  • 頂点x → 頂点(x+1)%K にコスト1の辺が出る
  • 頂点x → 頂点(x×10)%K にコスト0の辺が出る
  • コスト1を持って頂点1からスタートし、頂点0をゴールとする

K <= 105 よりダイクストラで解いてもO(KlogK)で間に合う。

この最短経路問題で解いているのは「mod K が x であるすべての正整数の集合における各桁の和の最小値」である。つまり0~(K-1)の各頂点が「mod K で x である正整数の集合」を表し、頂点1から頂点xまでの最短距離が「各桁の和の最小値」を表す。イメージとしては、「1」という数字からスタートしてその数に+1するか×10するかの遷移を繰り返している、という感じになる。頂点0が「modKが0であるすべての正整数の集合」、すなわちKの正の倍数ということになるので、頂点0までのコストが解となる。

感想

mod Kで0の集合 = Kの倍数の集合 というの自明に見えるけど、なんか頭の中で結びついてなかったので認識しておきたいですね

コード (D言語)

import std.stdio, std.array, std.string, std.conv, std.algorithm;
import std.typecons, std.range, std.random, std.math, std.container;
import std.numeric, std.bigint, core.bitop;

const int INF = 1 << 29;

void main() {
    auto N = readln.chomp.to!int;

    auto dist = new int[](N);
    dist.fill(INF);

    auto pq = new BinaryHeap!(Array!(Tuple!(int, int)), "a[1] > b[1]");
    pq.insert(tuple(1, 1));

    while (!pq.empty) {
        auto n = pq.front[0];
        auto c = pq.front[1];
        pq.removeFront;
        if (dist[n] <= c) continue;
        dist[n] = c;
        if (dist[(n+1)%N] > c+1) pq.insert(tuple((n+1)%N, c+1));
        if (dist[(n*10)%N] > c) pq.insert(tuple((n*10)%N, c));
    }

    dist[0].writeln;
}