题目大意
给定一个随机数生成器,其种子 seed 为 646464 位无符号整数。生成器函数 gen() 每次调用会更新 seed 并返回一个 [0,1)[0, 1)[0,1) 范围内的浮点数。你需要计算用给定种子生成前 nnn 个数的标准差。
输入
第一行是测试用例数 NNN(最多 404040 个)。每个测试用例一行,包含两个整数 nnn(1≤n≤1071 \le n \le 10^71≤n≤107)和 seedseedseed(0≤seed<2640 \le seed < 2^{64}0≤seed<264)。
输出
对每个测试用例,输出一行 Case #z: 后接标准差,保留 555 位小数。绝对误差不超过 10−410^{-4}10−4 视为正确。
生成器分析
题目给出的生成器代码如下(已转换为 C++\texttt{C++}C++ 风格):
unsigned long long seed;
long double gen() {
static const long double Z = (long double)1.0 / (1ULL << 32);
seed >>= 16;
seed &= (1ULL << 32) - 1;
seed *= seed;
return seed * Z;
}
关键步骤解析:
Z = 1.0 / 2^{32},所以返回值范围是 [0,1)[0, 1)[0,1)。seed >>= 16:右移 161616 位。seed &= (1ULL << 32) - 1:保留低 323232 位。seed *= seed:平方运算,可能导致溢出,但因为只保留低 646464 位,相当于模 2642^{64}264 乘法。- 返回
seed * Z,即一个 [0,1)[0, 1)[0,1) 的浮点数。
这个生成器是一个伪随机数生成器,其序列由初始种子唯一确定。
标准差计算公式推导
标准差的定义:
σ=1n∑i=1n(xi−μ)2
\sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i - \mu)^2}
σ=n1i=1∑n(xi−μ)2
其中 μ=1n∑i=1nxi\mu = \frac{1}{n} \sum_{i=1}^{n} x_iμ=n1∑i=1nxi。
展开平方项:
σ=1n∑i=1n(xi2−2μxi+μ2)
\sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i^2 - 2\mu x_i + \mu^2)}
σ=n1i=1∑n(xi2−2μxi+μ2)
=1n∑xi2−2μn∑xi+μ2n⋅n
= \sqrt{\frac{1}{n} \sum x_i^2 - \frac{2\mu}{n} \sum x_i + \frac{\mu^2}{n} \cdot n}
=n1∑xi2−n2μ∑xi+nμ2⋅n
=1n∑xi2−2μ2+μ2
= \sqrt{\frac{1}{n} \sum x_i^2 - 2\mu^2 + \mu^2}
=n1∑xi2−2μ2+μ2
=1n∑xi2−μ2
= \sqrt{\frac{1}{n} \sum x_i^2 - \mu^2}
=n1∑xi2−μ2
关键推导结果:
σ=∑xi2n−(∑xin)2
\sigma = \sqrt{\frac{\sum x_i^2}{n} - \left(\frac{\sum x_i}{n}\right)^2}
σ=n∑xi2−(n∑xi)2
因此,我们只需维护两个累加量:
sumX:∑xi\sum x_i∑xisumX2:∑xi2\sum x_i^2∑xi2
计算时:
- 均值
mean = sumX / n - 方差
variance = sumX2 / n - mean * mean - 标准差
stdDev = sqrt(variance)
注意:由于浮点数计算误差,variance 可能出现负值(极小负数),此时应将其置为 000。
算法设计
1. 直接计算法
- 对于每个测试用例,初始化
seed。 - 循环 nnn 次调用
gen(),累加sumX和sumX2。 - 计算均值、方差、标准差。
- 时间复杂度:O(n)O(n)O(n) 每用例。
- 空间复杂度:O(1)O(1)O(1)(只存累加量)。
2. 数值稳定性问题
- 当 nnn 很大(10710^7107)时,
sumX和sumX2可能非常大,但long double(通常 808080 位或 128128128 位)足以应对。 - 方差计算可能出现大数相减导致精度损失,但题目允许误差 10−410^{-4}10−4,可以接受。
3. 实现细节
- 使用
long double存储累加量和结果。 - 输出保留 555 位小数。
- 处理方差为负的情况(浮点误差)。
代码实现
// Standard Deviation
// UVa ID: 10886
// Verdict: Accepted
// Submission Date: 2025-12-01
// UVa Run Time: 0.410s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
unsigned long long seed; // 全局变量,gen() 会修改它
long double gen()
{
static const long double Z = (long double)1.0 / (1ULL << 32);
seed >>= 16;
seed &= (1ULL << 32) - 1;
seed *= seed;
return seed * Z;
}
int main()
{
int caseCount;
cin >> caseCount;
cout << fixed << setprecision(5);
for (int caseId = 1; caseId <= caseCount; ++caseId)
{
int n;
unsigned long long initSeed;
cin >> n >> initSeed;
seed = initSeed;
long double sumX = 0.0, sumX2 = 0.0;
for (int i = 0; i < n; ++i)
{
long double x = gen();
sumX += x;
sumX2 += x * x;
}
long double mean = sumX / n;
long double variance = sumX2 / n - mean * mean;
if (variance < 0) variance = 0; // 处理浮点误差
long double stdDev = sqrt(variance);
cout << "Case #" << caseId << ": " << stdDev << endl;
}
return 0;
}
复杂度分析
- 时间复杂度:O(∑n)O(\sum n)O(∑n),每个数生成一次,计算一次累加。
- 空间复杂度:O(1)O(1)O(1),只使用常数个变量。
对于最大的 n=107n = 10^7n=107,单个测试用例大约需要生成 10710^7107 个数,在合理时间内可以完成。
注意事项
- 浮点误差:方差计算可能出现极小负值,直接取 000 即可。
- 精度要求:使用
long double提高精度,输出保留 555 位小数。 - 生成器细节:注意
seed是全局变量,每个测试用例需要重新初始化。 - 输入输出:使用
cin/cout并关闭同步可加快速度(本题未要求,但可用于优化)。
总结
本题是一个典型的流式计算统计量问题,核心在于利用标准差的等价公式:
σ=∑xi2n−(∑xin)2
\sigma = \sqrt{\frac{\sum x_i^2}{n} - \left(\frac{\sum x_i}{n}\right)^2}
σ=n∑xi2−(n∑xi)2
从而避免存储所有数据,只需在线更新两个累加量。同时,理解伪随机数生成器的工作原理也是解题关键。
103

被折叠的 条评论
为什么被折叠?



