[R13E] 合成球2
- 难度 普及-
- 时限 1s
- 空限 512m
- 计数快速幂费马小定理
数据规模:(以十进制串给出),。
思路
与给定颜色序列、问合成方案数的题不同,本题问的是:每个球颜色任意取 ,有多少种初始情况最终能合成出颜色 。
关键是合成规则——新球颜色由你从合成前的两个球颜色中任选。于是:
- 只要初始情况中 至少有一个颜色为 的球,每轮合成时都把颜色 保留下来,最后一定得到颜色 ;
- 反之,若初始全是 ,则任你怎么合成都产生不出颜色 。
所以答案是「所有初始情况」减去「全不为 的初始情况」:
大指数取模
以最多 位的十进制串给出。模数 是质数, 与 都与 互质,由费马小定理 得
于是把 当字符串逐位算出 (每一位 y=(y*10+d) mod (P-1)),再用快速幂分别算 与 即可。逐位取模是 ,快速幂是 。
注意 时 ,公式仍然成立,无需特判;减法结果加模取模避免负数。
仓颉实现
import std.convert.*
import std.env.*
let MOD: Int64 = 998244353
func qpow(base: Int64, exp: Int64): Int64 {
var b = base % MOD
if (b < 0) {
b = b + MOD
}
var e = exp
var r: Int64 = 1
while (e > 0) {
if (e % 2 == 1) {
r = r * b % MOD
}
b = b * b % MOD
e = e / 2
}
return r
}
main(): Int64 {
let reader = getStdIn()
let line = reader.readln().getOrThrow()
let parts = line.split(" ", removeEmpty: true)
let nStr = parts[0]
let k = Int64.parse(parts[1])
// y = n mod (MOD-1),n 是超大十进制串
let phi = MOD - 1
var y: Int64 = 0
for (ch in nStr) {
let d = Int64(UInt32(ch) - UInt32(r'0'))
y = (y * 10 % phi + d % phi) % phi
}
let kk = k % MOD
let kk1 = (k - 1) % MOD
let ans = (qpow(kk, y) - qpow(kk1, y) + MOD) % MOD
println("${ans}")
return 0
}
要点:
- 题眼是看出「存在颜色 即可合成出颜色 」,把计数问题化成总数减补集。
- 超大 不能转整数,要用费马小定理把指数降到 范围内,逐位取模。
for (ch in nStr)得到的是UInt8字节,先经UInt32再Int64转成数字。