模拟赛题讲解[5]
来自 Roundgod 2022-07-26 noi.ac #2678
题目描述:
对于任意非负整数 \(x\) 和 \(m(2\le m\le 10)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之和。给定两个整数 \(k\) 和 \(m\) 。你需要计算在所有满足是 \(k\) 倍数的正数 \(x\) 中,最小的 \(f_m(x)\) 值是多少。
输入格式:
输入第一行包含两个整数 \(k,m(2\le m\le 10)\) 。
输出格式:
在一行中输出一个整数,表示答案。
输入1:
5 10
输出1:
1
输入2:
79992 10
输出2:
36
输入3:
4 5
输出3:
4
数据范围:
对于 \(30\%\) 的数据,\(1\le k\le 100\)
对于 \(100\%\) 的数据,\(1\le k\le 10^6\)
题解:
尝试以最小的花费构造一个数使得其是 \(k\) 的倍数
如果我们把这个构造的过程放在模 \(k\) 的意义下
问题就转化为了,如何以最小的花费构造一个 \(0\)
设 \(d_x,~x \in [0,k)\) 表示构造一个模 \(k\) 意义下为 \(x\) 的数的最小花费
然后尝试转移?
对于构造的数,我们可以乘 \(m\) ,也可以加 \(1\)
前者对应的花费为 \(0\) ,后者对应的花费为 \(1\)
为什么只加 \(1\) 呢?因为加 \(2\) 等价于两次加 \(1\)
于是,这个题就转化为了同余最短路
然后就是同余最短路,甚至不用 \(\text{Dijkstra}\) ,直接01bfs就好了
考场直接打暴力骗分,至今不是很理解同余最短路,有待补充
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e6+15)
int k,m,d[N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> k >> m;
for(int i=0; i<k; i++) d[i]=INF;
d[1]=1; deque<int> q;
q.push_back(1);
while(!q.empty())
{
int u=q.front(); q.pop_front();
if(!u){return cout << d[0] << '\n',0;}
int v=u*m%k; if(d[v]>d[u]){d[v]=d[u]; q.push_front(v);}
v=(u+1)%k; if(d[v]>d[u]+1){d[v]=d[u]+1; q.push_back(v);}
}
cout << d[0] << '\n';
return 0;
}