1 条题解
-
0
E. Expected Power 详细题解
题目回顾
给定数组 和概率数组 ,每个元素 以概率 被选入集合 。 设 为集合 的异或和,求 ,答案对 取模。
一、核心数学推导
1. 平方展开
设最终异或和为 ,其二进制表示为 (因为 ,仅需11位)。
平方展开:
$$x^2 = \left( \sum_{i=0}^{10} b_i 2^i \right) \left( \sum_{j=0}^{10} b_j 2^j \right) = \sum_{i=0}^{10}\sum_{j=0}^{10} b_i b_j \cdot 2^{i+j} $$对两边取期望:
$$E[x^2] = \sum_{i=0}^{10}\sum_{j=0}^{10} 2^{i+j} \cdot E[b_i b_j] $$关键结论
就是第 位和第 位同时为 1 的概率,我们只需要求这个概率即可。
2. 位对状态 DP
因为异或的每一位独立,我们对每一对位 单独维护 DP:
- :当前第 位异或结果为 ,第 位异或结果为 的概率。
- 初始状态:(未选任何数,两位都是 0)。
3. 转移规则
加入一个数 :
- 它的第 位是
- 它的第 位是
转移:
- 不选这个数:状态不变
- 选这个数:状态变为
最终答案:
$$ans = \sum_{i=0}^{10}\sum_{j=0}^{10} dp[i][j][1][1] \cdot 2^{i+j} $$
二、标程代码详细解析
#include <bits/stdc++.h> using namespace std; const int mod = 1e9 + 7; const int bits = 11; // a_i ≤ 1023,仅需 0~10 共11位 int dp[bits][bits][2][2]; // 核心DP:dp[i][j][x][y] // 快速幂:求逆元 int fast_exp(int b, int e, int mod) { int ans = 1; while(e) { if(e&1) ans = (1ll*ans*b) % mod; b = (1ll*b*b) % mod; e >>= 1; } return ans; } // 费马小定理求逆元 int inv(int n) { return fast_exp(n, mod-2, mod); } const int inverse_1e4 = inv(10000); // 1e4的逆元,固定预计算 // 状态转移:加入数字 a,概率 p void transition(int a, int p) { // 真实概率 = p / 1e4 p = (1ll * p * inverse_1e4) % mod; int negp = (mod + 1 - p) % mod; // 不选的概率 1-p // 取出 a 的每一位 int bin[bits]; for(int i = 0; i < bits; i++) { bin[i] = a & 1; a >>= 1; } // 对每一对 (i,j) 转移 for(int i = 0; i < bits; i++) { for(int j = 0; j < bits; j++) { int temp[2][2]; // 新状态 = 不选的情况 + 选的情况 for(int k : {0,1}) for(int l : {0,1}) temp[k][l] = (1ll*dp[i][j][k][l] * negp // 不选 + 1ll*dp[i][j][k^bin[i]][l^bin[j]] * p) % mod; // 选 // 覆盖更新 for(int k : {0,1}) for(int l : {0,1}) dp[i][j][k][l] = temp[k][l]; } } } int main() { ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while(t--) { int n; cin >> n; int a[n], p[n]; for(int i=0; i<n; i++) cin >> a[i]; for(int i=0; i<n; i++) cin >> p[i]; // DP初始化:所有位对初始都是 (0,0),概率1 for(int i=0; i<bits; i++) for(int j=0; j<bits; j++) dp[i][j][0][0] = 1; // 逐个元素转移 for(int i=0; i<n; i++) transition(a[i], p[i]); // 计算答案 int ans = 0; for(int i=0; i<bits; i++) { for(int j=0; j<bits; j++) { // 2^(i+j) int pw2 = (1ll << (i+j)) % mod; // 累加 E[b_i b_j] * 2^(i+j) ans = (ans + 1ll * pw2 * dp[i][j][1][1]) % mod; // 清空DP,准备下一组测试用例 for(int k : {0,1}) for(int l : {0,1}) dp[i][j][k][l] = 0; } } cout << ans << '\n'; } return 0; }
三、核心要点总结
1. 数学核心
$$E[x^2] = \sum_{i,j} 2^{i+j} \cdot P(\text{bit }i=1 \text{ 且 } bit\ j=1) $$2. DP 状态
- :第 位为 、第 位为 的概率
- 大小:,极小常数
3. 转移
- 选:概率 ,状态异或当前数的对应位
- 不选:概率 ,状态不变
4. 复杂度
完全可以通过 。
四、样例验证(第一个样例)
- ,概率各
- 最终 ,,
- 答案:
对 取模为 ,与样例一致。
最终总结
- 平方展开:把 拆成位对期望之和
- 位对 DP:维护每一对 的异或状态概率
- 转移简单:选/不选两种情况
- 复杂度极低:,标准高效解法
- 1
信息
- ID
- 6338
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者