【题解】 [NOIP 2002 普及组] 产生数

题目描述

题目大意

给定 \(k\) 个规则,规则为“使一位数可变换成另一个一位数”。求整数 \(n\) 根据规则经过若干次(可以为0次或多次)变化,能生成的整数个数。

思路

该题主要考察: Floyd传递闭包,单精度乘法。

显而易见,规则具有传递性。举个例子,1可变换成2,2可变换成3,则1可变换成3。
当然,自己也可以变换成自己。
所以,首先用Floyd传递闭包传递出转换关系。
接着,统计出每个一位数可以变换成的一位数的数量
最后,运用乘法原理,将整数 \(n\) 每一位的变换可能数相乘即可。

但是,需要注意的有2点:

  1. \(n < 10^{30}\),所以需要用字符串储存
  2. 答案可能过大,需要用单精度乘法进行运算。

代码

#include <bits/stdc++.h>
using namespace std;

string n; // 用字符串储存 n
int k, cnt[15]; // cnt[i] 储存 一位数i的变换可能数
bool a[15][15]; // a[i][j] 表示 i能否变换成j

int ans[1005]; // 记录答案(单精度)

int main()
{
    cin >> n >> k;
    while (k -- )
    {
        int u, v; cin >> u >> v;
        a[u][v] = 1;
    }

    // Floyd传递闭包
    for (int k = 1; k <= 9; k ++ )
    for (int u = 0; u <= 9; u ++ )
            for (int v = 1; v <= 9; v ++ )
                if (a[u][k] && a[k][v]) a[u][v] = 1;

    // 统计 变换可能数
    for (int u = 0; u <= 9; u ++ )
    {
        a[u][u] = 1; // 自己可以变换成自己
        for (int v = 0; v <= 9; v ++ )
            if (a[u][v]) cnt[u] ++ ;
    }

    int jw = 0; // 进位变量
    ans[0] = 1; // 答案初始值为1,即不进行变换
    for (int i = 0; n[i]; i ++ )
    {
        jw = 0; // 每次重置进位变量
        int num = cnt[n[i] - '0']; // 整数n第i位对应的变换可能数

        // 单精度乘法,将答案乘以 变换可能数
        for (int j = 0; j < 500; j ++ )
        {
            ans[j] = ans[j] * num + jw;
            jw = ans[j] / 10;
            ans[j] %= 10;
        }
    }

    // 处理前导0
    int i = 500;
    while (ans[i] == 0) i -- ;

    // 倒序输出
    for ( ; i >= 0; i -- ) cout << ans[i];
    cout << '\n';

    return 0;
}
posted @ 2024-08-13 20:33  T_泓  阅读(61)  评论(0编辑  收藏  举报