只跟踪前缀中数字 3 个数的奇偶,推出 答案 = (9*10^(N-1) + 7*8^(N-1))/2,在模 24690 下快速幂求分子再整除 2。
OJ: roj
题目 ID: 1313
难度:普及-
标签:数学计数递推python
创建: 2026-09-30 04:42
更新: 2026-10-05 09:13
题目描述
在所有的
输入样例:
text
2输出样例:
text
73思路
只关心数字
参考代码
Python 版:
python
#!/usr/bin/env python3
# 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-09-30 04:47
# update_at: 2026-09-30 04:47
import sys
MOD = 12345
HALF = 2 * MOD # 全程只在模 2*12345 下运算,最后整除 2 就等于对答案做精确减半
def even_count(n: int) -> int:
"""N 位数中含偶数个数字 3 的个数,对 12345 取模。
允许前导 0 时逐位转移是"9 种非 3 数字保持奇偶 + 数字 3 翻转奇偶",
解两状态递推得 偶数个 3 有 e_k=(10^k+8^k)/2 个、奇数个 3 有 (10^k-8^k)/2 个。
首位换成 1..9(其中只有 3 会翻转奇偶)后:
答案 = 8*e_{n-1} + o_{n-1} = (9*10^(n-1) + 7*8^(n-1)) / 2。
"""
doubled = (9 * pow(10, n - 1, HALF) + 7 * pow(8, n - 1, HALF)) % HALF
return doubled // 2 % MOD # 括号内恒为偶数,所以整除 2 是真减半而非取整
def solve() -> None:
n = int(sys.stdin.buffer.read())
print(even_count(n))
if __name__ == "__main__":
solve()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-05 09:11
* update_at: 2026-10-05 09:11
*/
// 求 N 位数中含偶数个数字 3 的个数,对 12345 取模。
// 逐位转移:这一位取 9 种非 3 数字时奇偶不变,取数字 3 时奇偶翻转。
// 设 e_k、o_k 为允许前导零时长度 k 的串中 3 的个数为偶/奇的个数,
// 递推解耦得 答案 = 8*e[N-1] + o[N-1] = (9*10^(N-1) + 7*8^(N-1)) / 2,
// 用快速幂按闭式直接计算,O(log N)。
#include <cstdio>
using namespace std;
typedef long long ll;
const ll MOD = 12345;
const ll HALF = 2 * MOD; // 分子恒为偶数,在模 2*MOD 下计算后真正整除 2,避免除法丢信息
ll n; // 位数 N
ll read_input() {
if (scanf("%lld", &n) != 1) return 0;
return n;
}
// 快速幂:a^e mod p
ll mod_pow(ll a, ll e, ll p) {
ll result = 1 % p; // p 可能为 1 时结果应为 0
a %= p;
while (e > 0) {
if (e & 1) result = result * a % p;
a = a * a % p;
e >>= 1;
}
return result;
}
void solve() {
// 分别在模 HALF 下求 10^(N-1)、8^(N-1),中间乘 9/7 后仍在 ll 范围内
ll pow10 = mod_pow(10, n - 1, HALF);
ll pow8 = mod_pow(8, n - 1, HALF);
ll doubled = (9 * pow10 % HALF + 7 * pow8 % HALF) % HALF; // 分子 9*10^(N-1)+7*8^(N-1)
ll ans = doubled / 2 % MOD; // 分子恒为偶数,先整除 2 再取模
printf("%lld\n", ans);
}
int main() {
if (read_input() == 0) return 0;
solve();
return 0;
}