[R83F] 构造全球化
数据规模:,, 的首位为 至 ,其余各位为 至 。
思路
把要求写成两条同余式。 设 ,名 的长度为 。姓在前得到的整数是 ,名在前得到的是 ,所以
关键观察:第二条只由 决定。 两条都是关于 的模 同余式,而第一条已经把 完全钉死为 。因此长度 可行,当且仅当「把 代入第二条成立」并且区间 内确实存在模 余 的数。下面把「代入后成立」化成好算的判据。
分解出 再整体除。 令 。由于 只有质因子 ,
把 反复除以 、 就能得到(,,故 )。
- 若 :因为 且 ,任何 都使 ,直接输出
NO。 - 若 :两边同除以 ,得 ,其中 。此时 与 互质:,而 ,两者不会同时为正, 的情形同理。求逆元即得
时第二条恒成立,取 。求 不需要大整数除法: 且 ,于是 。计算 时按 快速幂即可,不必真除。
合并两条同余,依据是 。 若 ,则 的任何解代回第二条都不成立,这个长度作废;反之若 ,则所有 都自动满足第二条(它们都 ),可行解集恰好就是一条模 的同余类。
固定长度取最小解。 类 中不小于 的最小数是 ,其中 。 是 位数当且仅当 ;因为 ,只要 就必然成立,只有 需要真的比较一次。 同时是长度为 的最小可行解,所以按 从小到大扫,第一次成功即为答案。
长度上界。 只取决于 。当 不小于 时(这个阈值不会超过 ), 对 的 、 部分都是 ,剩下的取值由 对 取模决定,其中 是 去掉 因子后的部分。 与 互质, 从 起就是纯周期的,周期不超过 。因此只要存在合法长度,就存在一个不超过 的合法长度;扫到 仍未成功即可输出 NO。
复杂度
读入 需要 ;检查长度需要 次常数运算;输出 。空间为 ,即读入的数字串与输出串。
仓颉实现
答案长度可达 位。因为 ,当长度较大时直接按「1 + 若干个 0 + 补足 位」拼出答案,不会产生进位。
查看仓颉实现
import std.env.*
func readNum(s: String): Int64 {
var x: Int64 = 0
for (ch in s) {
let c = Int64(ch)
if (c > 32) {
x = x * 10 + (c - 48)
}
}
return x
}
func powMod(b: Int64, e: Int64, md: Int64): Int64 {
var res: Int64 = 1 % md
var base: Int64 = b % md
var ex = e
while (ex > 0) {
if (ex % 2 == 1) {
res = res * base % md
}
base = base * base % md
ex /= 2
}
return res
}
func modInv(a: Int64, md: Int64): Int64 {
var r0 = md
var r1 = a % md
var t0: Int64 = 0
var t1: Int64 = 1
while (r1 != 0) {
let q = r0 / r1
let nr = r0 - q * r1
r0 = r1
r1 = nr
let nt = t0 - q * t1
t0 = t1
t1 = nt
}
var res = t0 % md
if (res < 0) {
res += md
}
return res
}
main() {
let reader = getStdIn()
let m = readNum(reader.readln().getOrThrow())
let sa = reader.readln().getOrThrow()
var am: Int64 = 0
var la: Int64 = 0
for (ch in sa) {
let c = Int64(ch)
if (c > 32) {
am = (am * 10 + (c - 48)) % m
la += 1
}
}
var v2: Int64 = 0
var t = m
while (t % 2 == 0) {
t /= 2
v2 += 1
}
var v5: Int64 = 0
t = m
while (t % 5 == 0) {
t /= 5
v5 += 1
}
var e2 = la
if (v2 < e2) {
e2 = v2
}
var e5 = la
if (v5 < e5) {
e5 = v5
}
var g: Int64 = 1
var i: Int64 = 0
while (i < e2) {
g *= 2
i += 1
}
i = 0
while (i < e5) {
g *= 5
i += 1
}
if (am % g != 0) {
println("NO")
} else {
let m2 = m / g
var r2: Int64 = 0
if (m2 > 1) {
let q = powMod(2, la - e2, m2) * powMod(5, la - e5, m2) % m2
let inv = modInv(q, m2)
let ag = am / g % m2
r2 = (m2 - ag * inv % m2) % m2
}
var pm: Int64 = 1 % m
var p: Int64 = 10 % m
var t10: Int64 = 1
var len: Int64 = 1
let limit = m2 + 60
var found = false
var delta: Int64 = 0
while (len <= limit && !found) {
let r1 = (m - am * p % m) % m
if (r1 % m2 == r2) {
var d = (r1 - pm) % m
if (d < 0) {
d += m
}
if (len > 7 || t10 + d < t10 * 10) {
found = true
delta = d
}
}
if (!found) {
pm = pm * 10 % m
p = p * 10 % m
if (t10 <= 100000000000000000) {
t10 *= 10
}
len += 1
}
}
if (!found) {
println("NO")
} else if (len <= 18) {
println(t10 + delta)
} else {
let sb = StringBuilder()
sb.append("1")
var z: Int64 = 0
while (z < len - 8) {
sb.append("0")
z += 1
}
let ds = delta.toString()
z = 0
while (z < 7 - ds.size) {
sb.append("0")
z += 1
}
sb.append(ds)
println(sb.toString())
}
}
}