[R22B]数字圆环


数据规模:1n501 \le n \le 50si{0,1}s_i \in \{0, 1\}

思路

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

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

sjk=0n12n1k=sj(2n1)s_j\cdot \sum_{k=0}^{n-1}2^{n-1-k}=s_j\cdot(2^n-1)

于是把所有位置相加:

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

其中 count1\text{count}_1 是字符串中 1 的个数。n50n\le50250505.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(): Int64 {
    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.toString())
    return 0
}