产生数 \operatorname{产生数} 产生数
题目链接: luogu P1037 \operatorname{luogu\ P1037} luogu P1037
题目
给出一个整数 n ( n < 1 0 30 ) n\ (n \lt 10^{30}) n (n<1030) 和 k k k 个变换规则 ( k ≤ 15 k \le 15 k≤15 )。
规则:
一位数可变换成另一个一位数。
规则的右部不能为零。
例如:
n
=
234
n=234
n=234 。有规则(
k
=
2
k=2
k=2 ):
- 2 2 2 -> 5 5 5
- 3 3 3 -> 6 6 6
上面的整数 234 234 234 经过变换后可能产生出的整数为(包括原数):
- 234 234 234
- 534 534 534
- 264 264 264
- 564 564 564
共 4 4 4 种不同的产生数。
现在给出一个整数 n n n 和 k k k 个规则。求出经过任意次的变换( 0 0 0 次或多次),能产生出多少个不同整数。
仅要求输出个数。
输入
第一行两个整数 n , k n,k n,k 。
接下来 k k k 行,每行两个整数 x i , y i x_i,y_i xi,yi 。
输出
输出能生成的数字个数。
样例输入
234 2
2 5
3 6
样例输出
4
思路
这道题就是一道 dfs 加高精。
我们可以对于 n n n 的每一位看看这一位可以转换到多少个数(包括本身),然后用高精乘把每一位可以转换到的数的个数都乘起来就是答案了。
有一点要注意的就是,最高位不能是 0 0 0 ,因为题目要求了。
代码
#include<cstdio>
#include<cstring>
using namespace std;
int n[101], k, num, x, y, turn[11][11], ans[1001], kk;
bool in[11];
int read() {//读入n
int an;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
n[++kk] = c - '0';
c = getchar();
}
return an;
}
void work(int now) {//找到能转换到的数
if (!in[now]) num++;
in[now] = 1;
for (int i = 1; i <= turn[now][0]; i++)
if (!in[turn[now][i]]) work(turn[now][i]);
}
int main() {
ans[1] = 1;
ans[0] = 1; //初始化
read();
scanf("%d", &k);//读入
for (int i = 1; i <= k; i++) {
scanf("%d %d", &x, &y);//读入
if (x != y) turn[x][++turn[x][0]] = y;//记录转换
}
for (int i = 1; i <= kk; i++) {
memset(in, 0, sizeof(in));//初始化
num = 0;
work(n[i]);//找到能转换到的数的个数
if (i == 1 && in[0]) num--;//最高位不可以是0(题目要求)
for (int i = 1; i <= ans[0]; i++)//高精乘
ans[i] *= num;
for (int i = 1; i <= ans[0] + 1; i++) {
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
}
if (ans[ans[0] + 1]) ans[0]++;//进位
}
for (int i = ans[0]; i >= 1; i--)
printf("%d", ans[i]);//输出
return 0;
}