P1037 [NOIP 2002 普及组] 产生数
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;
}