P1037 [NOIP 2002 普及组] 产生数
题目描述
给出一个整数 nnn 和 kkk 个变换规则。
规则:
- 一位数可变换成另一个一位数。
- 规则的右部不能为零。
例如:n=234,k=2n=234,k=2n=234,k=2。有以下两个规则:
- 2⟶52\longrightarrow 52⟶5。
- 3⟶63\longrightarrow 63⟶6。
上面的整数 234234234 经过变换后可能产生出的整数为(包括原数):
- 234234234。
- 534534534。
- 264264264。
- 564564564。
共 444 种不同的产生数。
现在给出一个整数 nnn 和 kkk 个规则。求出经过任意次的变换(000 次或多次),能产生出多少个不同整数。
仅要求输出个数。
输入格式
第一行两个整数 n,kn,kn,k,含义如题面所示。
接下来 kkk 行,每行两个整数 xi,yix_i,y_ixi,yi,表示每条规则。
输出格式
共一行,输出能生成的数字个数。
输入输出样例 #1
输入 #1
234 2
2 5
3 6
输出 #1
4
说明/提示
对于 100%100\%100% 数据,满足 n<1030n \lt 10^{30}n<1030,k≤15k \le 15k≤15。
【题目来源】
NOIP 2002 普及组第三题
对于这题,我们需要考虑每一个数字经过有限次变换可以有多少可能。即其路径上经过了多少个不同的结点。使用floyd-warshall解决这个传递闭包问题。
#include <bits/stdc++.h>
using namespace std;
vector<int> mul(const vector<int>& num, int m) {
if (m == 0) {
return {0};
}
vector<int> result;
int carry = 0;
for (int digit : num) {
int product = digit * m + carry;
result.push_back(product % 10);
carry = product / 10;
}
while (carry > 0) {
result.push_back(carry % 10);
carry /= 10;
}
return result;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string n_str;
int k;
cin >> n_str >> k;
bool g[10][10] = {false};
for (int i = 0; i < 10; ++i) {
g[i][i] = true;
}
for (int i = 0; i < k; ++i) {
int u, v;
cin >> u >> v;
g[u][v] = true;
}
for (int kk = 0; kk < 10; ++kk) {
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
if (g[i][kk] && g[kk][j]) {
g[i][j] = true;
}
}
}
}
int choices[10] = {0};
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
if (g[i][j]) {
choices[i]++;
}
}
}
vector<int> num = {1};
for (char c : n_str) {
int digit = c - '0';
num = mul(num, choices[digit]);
}
for (int i = num.size() - 1; i >= 0; --i) {
cout << num[i];
}
cout << endl;
return 0;
}