1. 程式人生 > 實用技巧 >[AtCoder Regular Contest 084] D - Small Multiple

[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\}\) 的建圖,可以構造表示出所有的數。進一步的,賦予邊權意義,跑最短路,可以得到一些有用的資訊。