首页 > 试题广场 >

变化的数组

[编程题]变化的数组
  • 热度指数:1674 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 512M,其他语言1024M
  • 算法知识视频讲解
\hspace{15pt}对于给定的长度为 n 的数组 \{a_1,a_2,\dots,a_n\} 和一个整数 m ,保证全部非负。你需要执行 k 次操作,每一次操作如下:
\hspace{22.5pt}\bullet\对数组中的每一个元素 a_i ,投掷一次硬币,若硬币为正则将这个元素修改为 a_i + (a_i \operatorname{and} m) ;反之,则不操作;
\hspace{15pt}在全部 k 次操作完成后,求解数组元素和的期望。

\hspace{15pt}在本题中,\operatorname{and} 运算即按位与运算。如果您需要更多位运算相关的知识,可以参考 OI-Wiki上的相关章节 。

输入描述:
\hspace{15pt}第一行输入三个整数 n,m,k \left( 1\leqq n \leqq 10^5;\ 1 \leqq m, k \leqq 10^9 \right) 代表数组中的元素数量、修改公式中的定值、操作次数。
\hspace{15pt}第二行输入 n 个整数 a_1,a_2,\dots,a_n \left( 0 \leqq a_i \leqq 10^9 \right) 代表数组元素。


输出描述:
\hspace{15pt}在一行上输出一个整数,代表 k 次操作完成后数组元素和的期望

\hspace{15pt}可以证明答案可以表示为一个不可约分数 \frac{p}{q} ,为了避免精度问题,请直接输出整数 \left(p \cdot q^{-1} \bmod M\right) 作为答案,其中 M = (10^9 + 7)q^{-1} 是满足 q\times q^{-1} \equiv 1 \pmod{M} 的整数。
示例1

输入

2 6 1
3 5

输出

11

说明

\hspace{15pt}全过程模拟如下:
\hspace{22.5pt}\bullet\ \frac{1}{4} 的概率第一个元素硬币为正、第二个元素硬币也为正,答案为 \frac{1}{4} \times \big(3 + (3 \operatorname{and} m) + 5 + (5 \operatorname{and} m) \big) =\frac{14}{4}
\hspace{22.5pt}\bullet\ \frac{1}{4} 的概率第一个元素硬币为正、第二个元素硬币为反,答案为 \frac{1}{4} \times \big(3 + (3 \operatorname{and} m) + 5 \big) =\frac{10}{4}
\hspace{22.5pt}\bullet\ \frac{1}{4} 的概率第一个元素硬币为反、第二个元素硬币为正,答案为 \frac{1}{4} \times \big(3 + 5 + (5 \operatorname{and} m) \big) =3
\hspace{22.5pt}\bullet\ \frac{1}{4} 的概率第一个元素硬币为反、第二个元素硬币也为反,答案为 \frac{1}{4} \times \big(3 + 5\big) =2
\hspace{15pt}综上,期望为 11
示例2

输入

3 1 4
1 1 1

输出

312500008
package main

import (
	"bufio"
	"fmt"
	"os"
)

/* 
	分别求每一个数的期望
	其中,考虑到and的性质,一个数变化一定次数后,值会变为0,后续值不再改变
 */

const MOD = 1000000007

// 快速幂:计算 (base^exp) % MOD
func powMod(base, exp, mod int64) int64 {
	var result int64 = 1
	for exp > 0 {
		if exp&1 == 1 {
			result = (result * base) % MOD
		}
		base = (base * base) % MOD
		exp >>= 1
	}
	return result
}

// 计算 C(k, t) mod MOD,k 可能很大,但 t 很小
func comb(k, t int64) int64 {
	/*
		费马小定理
		MOD为质数,den != 0,den^(MOD-1) = 1 (mod MOD)
		den模MOD为1的逆为(den^(MOD-2))%MOD
	*/
	if t == 0{
		return 1
	}
	if t < 0 || k < t  {
		return 0
	}

	var m, n int64 = 1, 1 // 分子、分母
	for i := int64(0); i < t; i++ {
		m = m * ((k - i) % MOD)% MOD
		n = n * (i + 1) % MOD
	}
	n_ := powMod(n, MOD-2, MOD)
	return m * n_ % MOD
}

// 模拟一个数 x 在操作下的演化路径
func evolvePath(x, m int64) []int64 {
	path := []int64{}
	for {
		path = append(path, x)
		andVal := x & m
		if andVal == 0 { // 变化一定次数后会变为0
			break
		}
		x = x + andVal
	}
	return path
}

// 计算单个数 x 经过 k 次操作后的期望值
func expectedOne(x, m, k int64) int64 {
	path := evolvePath(x, m)
	var T int64 = int64(len(path))

	var num2k int64 = powMod(2, k, MOD)
	var num2k_ int64 = powMod(num2k, MOD-2, MOD)

	var result, front, t int64 = 0, 0, 0
	for i := int64(0); i < T-1; i++ { // 前T-1个,
		t = comb(k, i)
		result = (result + path[i] * t) %MOD
		front = (front + t)%MOD
	}

	result = (result + path[T-1] * ((num2k - front+MOD)%MOD)) % MOD
	return result * num2k_ % MOD
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	var n, m, k int64
	fmt.Fscanf(reader, "%d %d %d\n", &n, &m, &k)

	var a = make([]int64, n)
	for i := int64(0); i < n; i++ {
		fmt.Fscanf(reader, "%d", &a[i])
	}

	var result int64 = 0
	for i := int64(0); i < n; i++ {
		result = (result + expectedOne(a[i], m, k)) % MOD
	}

	fmt.Println(result)
}

发表于 2025-09-01 23:33:06 回复(0)