把数字变换规则建成 10 个点的有向图,用位集 Floyd 求传递闭包,答案为每位可达数字个数的乘积(unsigned __int128)。
OJ: roj
题目 ID: 3514
难度:普及
标签:图论传递闭包乘法原理python
创建: 2026-10-02 04:41
更新: 2026-10-06 12:40
题目描述
给定整数 n k,接下来 x y;输出:一个整数。样例:输入 234 2、规则 2 5、3 6 时输出 4(234、534、264、564)。
思路
把数字 unsigned __int128 计算。
参考代码
Python 版:
python
import sys
from functools import reduce
from operator import mul
# g[d]:位集,d 经任意次变换能到达的数字个数(含 d 自身)
g = [1 << d for d in range(10)]
data = iter(sys.stdin.buffer.read().split()) # 全部读入后用 next() 顺序消费(兼容 \r\n 行尾)
n = next(data).decode() # n 最多 30 位,要逐位访问,按字符串读
k = int(next(data))
for i in range(k):
x, y = int(next(data)), int(next(data))
g[x] |= 1 << y
for m in range(10): # Floyd 传递闭包:若 i->j 且 j 可达 d,则 i 可达 d
for i in range(10):
if g[i] >> m & 1:
g[i] |= g[m]
# 答案 = 每一位可选数字个数之积(n 最大 30 位,乘积用 Python 大整数)
print(reduce(mul, (bin(g[int(c)]).count('1') for c in n)))C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 12:40
* update_at: 2026-10-06 12:41
*/
#include <cstdio>
#include <string>
#include <iostream>
typedef long long ll;
// g[d]:位集,第 e 位为 1 表示数字 d 经任意次变换能变成数字 e(初始含自身,允许 0 次变换)
ll g[10];
// s:数字 n(最多 30 位),按字符串逐位访问;k:规则条数
std::string s;
ll k;
int main() {
std::cin >> s >> k;
for (ll d = 0; d < 10; d++) g[d] = (ll)1 << d; // 初始 g[d] 只含 d 自己
for (ll i = 1; i <= k; i++) {
ll x, y;
std::cin >> x >> y; // 规则 x -> y,加入一条有向边
g[x] |= (ll)1 << y;
}
// Floyd 求传递闭包:若 i 能到 m 且 m 能到 e,则 i 能到 e
for (ll m = 0; m < 10; m++)
for (ll i = 0; i < 10; i++)
if (g[i] >> m & 1) g[i] |= g[m];
// 每一位数字 d 有 popcount(g[d]) 种取值,各位独立,由乘法原理连乘
// n 最多 30 位、每位最多 10 种,答案可达 10^30,超出 ll,用 unsigned __int128 输出
unsigned __int128 ans = 1;
for (ll i = 0; i < (ll)s.size(); i++) ans *= __builtin_popcountll(g[s[i] - '0']);
// __int128 无法直接用 cout 输出,转成十进制字符串打印
std::string out;
do {
out += char('0' + (int)(ans % 10));
ans /= 10;
} while (ans > 0);
for (ll i = (ll)out.size() - 1; i >= 0; i--) putchar(out[i]);
putchar('\n');
return 0;
}