[NOIP2002 普及组] 产生数
题目描述
给出一个整数 和
个变换规则。
规则:
- 一位数可变换成另一个一位数。
- 规则的右部不能为零。
例如:。有以下两个规则:
。
。
上面的整数 经过变换后可能产生出的整数为(包括原数):
。
。
。
。
共 种不同的产生数。
现在给出一个整数 和
个规则。求出经过任意次的变换(
次或多次),能产生出多少个不同整数。
仅要求输出个数。
输入格式
第一行两个整数 ,含义如题面所示。
接下来 行,每行两个整数
,表示每条规则。
输出格式
共一行,输出能生成的数字个数。
样例 #1
样例输入 #1
234 2
2 5
3 6样例输出 #1
4提示
对于 数据,满足
,
。
【题目来源】
NOIP 2002 普及组第三题
思路
每个数字作为图中的一个节点,将每个数字可以到达的后继数字连成一条有向边,形成一个有向图。
先用深度优先搜索计算每个数字能够到达的节点数,然后根据数字字符串中每个数字能够到达的节点数,利用排列组合计算求出经过任意次的变换( 次或多次),能产生出不同整数的个数。
因为方案数可能非常大,用__int128 类型用于存储方案数。write 函数用于输出 __int128 类型的整数。
AC代码
#include <iostream>
#include <list>
#include <bitset>
#define ll long long
#define lll __int128
#define AUTHOR "HEX9CF"
using namespace std;
const int N = 15;
list<int> v[N];
bitset<N> vis;
int cnt[N];
void dfs(int x)
{
vis[x] = 1;
for (const auto &i : v[x])
{
if (!vis[i])
{
dfs(i);
}
}
}
void write(lll x)
{
if (x / 10)
{
write(x / 10);
}
putchar(x % 10 + '0');
}
int main()
{
string n;
int k;
lll ans;
ans = 1;
cin >> n >> k;
for (int i = 0; i < k; i++)
{
int a, b;
cin >> a >> b;
v[a].push_front(b);
}
for (int i = 0; i < 10; i++)
{
vis.reset();
dfs(i);
cnt[i] = vis.count();
// cout << i << " " << cnt[i] << endl;
}
for (const auto &i : n)
{
// cout << i << endl;
ans *= cnt[i - '0'];
}
// cout << ans << endl;
write(ans);
return 0;
}
















