[R22B]数字圆环


数据规模:1≤n≤501 \le n \le 50,si∈{0,1}s_i \in \{0, 1\}。

思路

从位置 ii 出发顺时针走一周得到的 nn 位二进制数,其第 kk 位(最高位为第 00 位)对应圆环上的数字 s(i+k) mod ns_{(i+k)\bmod n},权重为 2n−1−k2^{n-1-k}。

固定某个位置 jj 上的数字 sjs_j,当起始位置 ii 取遍 0..n−10..n-1 时,jj 在所得二进制数中的位次 k=(j−i) mod nk=(j-i)\bmod n 也取遍 0..n−10..n-1,所以 sjs_j 对总和的总贡献为:

sj⋅∑k=0n−12n−1−k=sj⋅(2n−1)s_j\cdot \sum_{k=0}^{n-1}2^{n-1-k}=s_j\cdot(2^n-1)

于是把所有位置相加:

答案=(2n−1)⋅count1\text{答案}=(2^n-1)\cdot \text{count}_1

其中 count1\text{count}_1 是字符串中 1 的个数。n≤50n\le50,250⋅50≈5.6×10162^{50}\cdot 50\approx5.6\times10^{16},64 位整数足够。

复杂度

时间 O(n)O(n),空间 O(1)O(1)。

仓颉实现

查看仓颉实现
import std.env.*
import std.convert.*

main() {
    let reader = getStdIn()
    let n = Int64.parse(reader.readln().getOrThrow())
    let s = reader.readln().getOrThrow()

    // 统计 '1' 的个数
    var cnt: Int64 = 0
    for (r in s.toRuneArray()) {
        if (r == r'1') {
            cnt += 1
        }
    }

    // 计算 2^n - 1
    var pow: Int64 = 1
    for (_ in 0..n) {
        pow = pow * 2
    }

    let ans = cnt * (pow - 1)
    println(ans)
}