kurarrr's memooo

主に競プロ備忘録

ARC 084 D - Small Multiple

問題概要

Kの正の倍数の10進数での各桁の和として最小のものを求めよ。
2<=K<=105

解法

1を以下の2種類の操作でKの倍数にすることを考える。
1. 1増やす
2. 10倍する
この2種類の操作で全ての自然数が作れる。
また、1の位の数が9の時には1を使わないとすると(2を使えばそうする必要はないため)、
各桁の和は1,2の操作で
1. 1増える
2. 変わらない
よってmod 0〜K-1のノードを作り、1,2の操作に対応する辺(距離は各々1,0)を張る。
その時mod 1 のノードからmod 0のノードへの最短路+1が答え。
Dijkstra法を用いればO(KlogK)で求められるので十分間に合う。

解説動画を見たらアア…ってなった。

メモ

Kの倍数 -> mod Kのグラフで考える。
知らないと絶対できなさそう。

コード

#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<algorithm>
#include<utility>
#include<memory>
#include<cmath>
#include<stack>
#include<tuple>
#include<numeric>
#include<cassert>

#define ALL(g) (g).begin(),(g).end()
#define REP(i, x, n) for(int i = x; i < n; i++)
#define rep(i,n) REP(i,0,n)
#define EXIST(s,e) ((s).find(e)!=(s).end())
#define pb push_back
#define DEBUG false

using namespace std;

using ll = long long;
using P = pair<int,int>;
using Pl = pair<int,ll>;

const int mod=1e9+7,INF=1<<30;
const double EPS=1e-12,PI=3.1415926535897932384626;
const ll LINF=1LL<<60;
const int MAX_K = 100005;
const ll lmod = 1e9+7;

int K;

class edge{
  public:
    ll cost;int to;
  edge(ll cost,int to)
    :cost(cost),to(to){}
};
vector<edge> e[MAX_K];
ll d1[MAX_K];

void dijkstra(int start,vector<edge> graph[],ll d[]){
  priority_queue<Pl,vector<Pl>,greater<Pl>> que;
  fill(d,d+MAX_K,LINF);
  d[start] = 0LL;
  que.push(Pl(0LL,start));
  while(!que.empty()){
    P p=que.top(); que.pop();
    int v=p.second; //行き先
    if(d[v]<p.first)  continue;
    rep(i,graph[v].size()){
      edge e=graph[v][i];
      if(d[v]+e.cost<d[e.to]){
        d[e.to]=d[v]+e.cost;
        que.push(P(d[e.to],e.to));
      }
    }
  }
  return ;
}



int main(){
  cin >> K ;
  rep(i,K){
    e[i].pb(edge(1LL,(i+1)%K));
  }
  REP(i,1,K){
    e[i].pb(edge(0LL,(i*10)%K));
  }
  dijkstra(1,e,d1);
  cout << d1[0] + 1LL << endl;
  return 0;
}