洛谷 P1037 [NOIP 2002 普及组] 产生数 题解

洛谷 P1037 [NOIP 2002 普及组] 产生数 题解

题目描述

原题链接:洛谷 P1037 [NOIP 2002 普及组] 产生数

给出一个整数 \(n\) 和 \(k\) 个变换规则。

规则:

  • 一位数可变换成另一个一位数。
  • 规则的右部不能为零。

现在给出一个整数 \(n\) 和 \(k\) 个规则。求出经过任意次的变换(\(0\) 次或多次),能产生出多少个不同整数。(\(n \lt 10^{30}\),\(k \le 15\))

题意分析

学完 DFS 和 BFS 之后,我就做这道题。蒟蒻做了整半天,结果40pts,于是默默地按下了查看题解。

这道题其实就是图论与高精度的综合运用。

一、图的输入与遍历

使用邻接表存储输入数据。例如,当输入:

1234 3
2 3
3 2
3 5

我们可以构造出如下的有向图:

graph

要想知道数字 \(2\) 可以变成哪些点,直接以数字 \(2\) 为起点遍历这个图,并统计经过的顶点数量即可。根据乘法原理,将 \(n\) 的每一位数字的遍历结果相乘即为方案总数。

示例代码

  1. 深度优先遍历
#include <vector>
using namespace std;

vector<int> G[10]; // 因为只有 10 个一位数,所以图也只有 10 个顶点
bool vis[10]; // vis[i]=0 表示 i 没有被访问过,vis[i]=1 表示 i 被访问过

int dfs(int x) {
    vis[x] = 1;
    int cnt = 1;
    for (int i = 0; i < G[x].size(); i++)
        if (!vis[G[x][i]])
            cnt += dfs(G[x][i]); // 如果 G[x][i] 没有被访问过,则访问这个数
    return cnt;
}
  1. 广度优先遍历
#include <vector>
#include <queue>
using namespace std;

vector<int> G[10];
queue<int> Q;
bool vis[10];

int bfs(int x) {
    vis[x] = 1;
    int cnt = 0;
    while (!Q.empty()) {
        int y = Q.front();
        Q.pop();
        cnt++;
        for (int i = 0; i < G[y].size(); i++)
            if (!vis[G[y][i]]) {
                vis[G[y][i]] = 1;
                Q.push(G[y][i]);
			}
	}
	return cnt;
}

二、高精度乘法

每一位数字最多有 \(10\) 种变换方式,\(n\) 最多有 \(30\) 位,所以方案总数最多为 \(10^{30}\),即便是 unsigned long long 也存不下这么大的数字,所以我们需要使用高精度乘低精度。

我们使用封装结构体并重载运算符的方式来编写程序,使用时更加简洁。

struct Bigint {
    int len, a[35]; // 为了兼顾效率与代码复杂度,用 len 记录位数,a 记录每个数位

    Bigint(int x = 0) { // 通过初始化使得这个大整数能够表示整型 x,默认为 0
        memset(a, 0, sizeof(a));
        for (len = 1; x; len++)
            a[len] = x % 10, x /= 10;
        len--;
    }

    int &operator[](int i) {
        return a[i]; // 重载[], 可以直接用 x[i] 代表 x.a[i], 编写时更加自然
    }

    void flatten(int L) { //一口气处理 1 到 L 范围内的进位并重置长度。需要保证 L 不小于有效长度
        // 因为相当于把不是一位数的位都处理成一位数, 故取名为“展平”
        len = L;
        for (int i = 1; i <= len; i++)
            a[i + 1] += a[i] / 10, a[i] %= 10;
        for (; !a[len]; ) // 重置长度成为有效长度
            len--;
    }

    void print() { // 输出
        for (int i = max(len, 1); i >= 1; i--)
            printf("%d", a[i]);
    }
};

Bigint operator*(Bigint a, int b) { // 表示 Bigint 类乘整型变量, 返回一个 Bigint 类
    Bigint c;
    int len = a.len;
    for (int i = 1; i <= len; i++)
        c[i] = a[i] * b; // 计算贡献
    c.flatten(len + 2); // dfs 函数返回值最多 2 位, 所以可以这样做一遍“展平”处理进位
    return c;
}

代码实现

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

struct Bigint {
    int len, a[35];

    Bigint(int x = 0) {
        memset(a, 0, sizeof(a));
        for (len = 1; x; len++)
            a[len] = x % 10, x /= 10;
        len--;
    }

    int &operator[](int i) {
        return a[i];
    }

    void flatten(int L) {
        len = L;
        for (int i = 1; i <= len; i++)
            a[i + 1] += a[i] / 10, a[i] %= 10;
        for (; !a[len]; )
            len--;
    }

    void print() {
        for (int i = max(len, 1); i >= 1; i--)
            printf("%d", a[i]);
    }
};

Bigint operator*(Bigint a, int b) {
    Bigint c;
    int len = a.len;
    for (int i = 1; i <= len; i++)
        c[i] = a[i] * b;
    c.flatten(len + 2);
    return c;
}

vector<int> G[10];
queue<int> Q;
bool vis[10];

int n[40], k, len;
char n1[40];
Bigint ans(1);

int dfs(int x) {
    vis[x] = 1;
    int cnt = 1;
    for (int i = 0; i < G[x].size(); i++)
        if (!vis[G[x][i]])
            cnt += dfs(G[x][i]); // 如果 G[x][i] 没有被访问过,则访问这个数
    return cnt;
}

int bfs(int x) {
    vis[x] = 1;
    Q.push(x);
    int cnt = 0;
    while (!Q.empty()) {
        int y = Q.front();
        Q.pop();
        cnt++;
        for (int i = 0; i < G[y].size(); i++)
            if (!vis[G[y][i]]) {
                vis[G[y][i]] = 1;
                Q.push(G[y][i]);
			}
	}
	return cnt;
}

int main() {
    scanf("%s%d", n1, &k);
    len = strlen(n1);
    for (int i = 0; i < len; i++)
        n[i] = n1[i] - '0';
    for (int i = 0; i < k; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        G[x].push_back(y);
    }
    for (int i = 0; i < len; i++) {
        memset(vis, 0, sizeof(vis));
        ans = ans * dfs(n[i]);
//      ans = ans * bfs(n[i]);
    }
    ans.print();
    return 0;
}
posted @ 2026-08-26 21:55  zzx321  阅读(17)  评论(0)    收藏  举报