[AtCoder Regular Contest 084] D - Small Multiple(同余最短路)
Problem
Solution
性质1:若 \(x \equiv 0 \pmod{K}\) 且 \(x \neq 0\) 则说明 \(x\) 是 \(K\) 的倍数。
性质2: \((a*10+b)*10+c \equiv ((a*10+b)\%K) *10+c \pmod{K}\)
根据以上性质,我们可以把 \(0\) 到 \(K-1\) 这 \(K\) 个数看成 \(K\) 个点。其中 \(1\) 到 \(K\) 每个点 \(x\) 向 \((x*10+j)\),其中\(j \in \{0,1,2,...,9\}\),建 \(9\) 条边,每条边的权值是 \(j\)。从 \(1\) 到 \(9\) 每个点一起出发跑最短路(多源最短路)。答案就是到 \(0\) 这个点的最短路。
Code
Talk is cheap.Show me the code.
#include<bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
typedef pair<int,int> PII;
const int N = 1e6+7;
int K,cnt;
int head[N],dist[N];
bool vis[N];
struct Edge {
int next,to,w;
}edge[N<<1];
inline void add(int u,int v,int w) {
edge[++cnt] = (Edge)<%head[u],v,w%>;
head[u] = cnt;
}
void Dijkstra() {
priority_queue<PII> q;
memset(dist, 0x3f, sizeof(dist));
for(int i=1;i<=9;++i) {
dist[i] = i; q.push(mp(-dist[i], i));
}
while(!q.empty()) {
int u = q.top().se; q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i=head[u];i;i=edge[i].next) {
int v = edge[i].to, w = edge[i].w;
if(dist[u]+w < dist[v]) {
dist[v] = dist[u]+w; q.push(mp(-dist[v], v));
}
}
}
}
int main()
{
K = read();
for(int i=1;i<K;++i) {
for(int j=0;j<=9;++j) {
int u = i, v = (i*10 + j) % K, w = j;
add(u,v,w);
}
}
Dijkstra();
printf("%d\n",dist[0]);
return 0;
}
/*
79992
36
*/
Summary
-
倍数放在模意义下为0,由此进行思考的套路。
-
以 1-9 为起点,形如 \(x -> x*10 + j , j \in \{0,1,2,...,9\}\) 的建图,可以构造表示出所有的数。进一步的,赋予边权意义,跑最短路,可以得到一些有用的信息。