【发布时间】:2014-11-16 23:12:35
【问题描述】:
我正在尝试解决以下任务:
1) 给定大小为 N 的数组 A。
2) 给定一组 范围更新 查询,即 (L, R, val) 应该为 L A[i] += val。
3) 给定一组 range sum 查询,即 (L, R) 应该返回 sum(A[i]) for L
约束:
1) A、段和查询集的大小 N, N1, N2 2) 0
问题是计算 所有范围求和查询的总和 (S) 以 2^32 为模。
似乎人们可以实现 Segment tree 以在 O(NlogN) 时间内获得所需的总和,但实际上我们 不 > 需要用到这个数据结构。相反,我们可以用 2 或 3 个数组在 O(N) 时间内以某种方式计算 S。这里的一般想法是什么?
我最近用 C++ 编写了一些算法来解决这个问题,但这并不是最优的。伪代码:
- 创建两个数组 Add[0..N-1] 和 Substract[0..N-1]。
- 遍历范围更新集并执行 Add[L] += val 和 Substract[R] += val。
- 创建数组 Partial_sum[0..N]
- Partial_sum[0] = 0,what_to_add = 0。
- 对于 [1..N] 中的 i:
5.1。 Partial_sum[i] = Partial_sum[i - 1] + Add[i - 1] + what_do_add
5.2. what_do_add = what_to_add + Add[i - 1] - Substract[i - 1]
我们得到 Partial_sum 数组,并且可以像 Partial_sum[R+1] 一样在 O(1) 时间内轻松计算任何段总和 (L, R) - Partial_sum[L].
但是,问题在于第 2 步太慢了。此外,第 5 步中的循环很难理解。那是 O(n) 解决方案,但常数太高了。我知道应该有改进第 5 步的方法,但我不明白如何做到这一点。
有人可以提出一些想法甚至建议他们自己的算法来解决这个问题吗?
谢谢。
我的算法实现:
#include <cstring>
#include <iostream>
#include <stdio.h>
typedef unsigned int UINT;
typedef unsigned long long ULL;
//MOD and size of A
const ULL MOD = 4294967296LL; // 2^32
const size_t N = 16777216; // 2^24
//params for next_rand()
UINT seed = 0;
UINT a;
UINT b;
//get random segment
UINT next_rand()
{
seed = seed * a + b;
return seed >> 8;
}
int main()
{
UINT N1, N2;
std::cin >> N1 >> N2;
std::cin >> a >> b;
UINT* add = new UINT[N]; //Add array
UINT* subs = new UINT[N]; //Substraction array
UINT* part_sum = new UINT[N + 1]; //Partial sums array
memset(add, 0, sizeof(UINT) * N);
memset(subs, 0, sizeof(UINT) * N);
memset(part_sum, 0, sizeof(UINT) * (N + 1)); //Initialize arrays
//step 2
for (size_t i = 0; i < N1; ++i)
{
UINT val = next_rand();
UINT l = next_rand();
UINT r = next_rand();
if (l > r)
{
std::swap(l, r);
}
add[l] = (add[l] + val);
subs[r] = (subs[r] + val);
}
part_sum[0] = 0;
UINT curr_add = 0;
//step 5
for (size_t i = 1; i <= N; ++i)
{
part_sum[i] = (part_sum[i - 1] + curr_add + add[i - 1]);
curr_add = (curr_add + add[i - 1] - subs[i - 1]);
}
UINT res_sum = 0;
//Get any segment sum in O(1)
for (size_t i = 0; i < N2; ++i)
{
UINT l = next_rand();
UINT r = next_rand();
if (l > r)
{
std::swap(l, r);
}
res_sum = (res_sum + part_sum[r + 1] - part_sum[l]);
}
std::cout << res_sum;
delete []add;
delete []subs;
delete []part_sum;
return 0;
}
【问题讨论】:
-
请展示您目前的代码以进行改进。
-
如果 next_rand 返回越界索引怎么办?为什么需要随机过程?也许我不明白这个问题。
-
你可以避免使用 next_rand() 函数,因为它总是从 [0, 2^24 - 1] 返回值。问题出在算法上。如果 N1 = 2^24, N2 = 2^24,它的工作速度太慢了。应该有只使用 2 个数组而不是 3 个数组并更清楚地执行第 5 步的方法,但我不知道如何。
-
在 next_rand() 中,种子可以超过 2^24,因为它是无符号整数,所以我们有模 2^32 算术。当我们这样做时 (seed >> 8) 我们得到的值不超过 2^32/2^8 = 2^24
标签: c++ arrays algorithm sum time-complexity