【发布时间】:2014-11-14 09:45:02
【问题描述】:
给定一个数组 A,其中包含 N 个元素和 N 个范围,每个范围的形式为 [L, R]。将范围的值称为A中从索引L到索引R的所有元素的总和。
示例:设数组 A = [2 5 7 9 8],给定范围为 [2,4],则该范围的值为 5+7+9=21
现在我们得到了 Q 查询,每个查询都是 2 种类型之一:
1. 0 X Y : It means change Xth element of array to Y.
2. 1 A B : It means we need to report the sum of values of ranges from A to B.
示例:让数组 A = [2 3 7 8 6 5] 并让我们有 3 个范围:
R1: [1,3] Then value corresponding to this range is 2+3+7=12
R2: [4,5] Then value corresponding to this range is 8+6=14
R3: [3,6] Then value corresponding to this range is 7+8+6+5=26
现在让我们有 3 个查询:
Q1: 1 1 2
Then here answer is value of Range1 + value of Range2 = 12+14=26
Q2: 0 2 5
It means Change 2nd element to 5 from 3.It will change the result of Range 1.
Now value of Range1 becomes 2+5+7=14
Q3: 1 1 2
Then here answer is value of Range1 + value of Range2 = 14+14=28
如果我们有 10^5 个查询并且 N 也高达 10^5,该怎么做。如何高效地向 Queries2 报告?
我的方法:第一个查询很容易处理。我可以从数组中构建一个段树。我可以用它来计算第一个数组(第二个数组中的一个元素)中间隔的总和。但是我如何处理 O(log n) 中的第二个查询?在最坏的情况下,我更新的元素将在第二个数组的所有区间中。
我需要 O(Qlog N) 或 O(Q(logN)^2) 解决方案。
显然我们不能为每个查询都有一个 O(N)。所以请帮助获得有效的方法
我当前的代码:
#include<bits/stdc++.h>
using namespace std;
long long arr[100002],i,n,Li[100002],Ri[100002],q,j;
long long queries[100002][2],query_val[100002],F[100002],temp;
long long ans[100002];
int main()
{
scanf("%lld",&n);
for(i=1;i<=n;i++)
scanf("%lld",&arr[i]);
for(i=1;i<=n;i++)
{
scanf("%lld%lld",&Li[i],&Ri[i]);
}
for(i=1;i<=n;i++)
{
F[n] = 0;
ans[i] = 0;
}
scanf("%lld",&q);
for(i=1;i<=q;i++)
{
scanf("%lld",&query_val[i]);
scanf("%lld%lld",&queries[i][0],&queries[i][1]);
}
for(i=1;i<=n;i++)
{
for(j=Li[i];j<=Ri[i];j++)
{
F[i] = F[i] + arr[j];
}
}
long long diff;
long long ans_count = 0,k=1;
for(i=1;i<=q;i++)
{
if(query_val[i] == 1)
{
temp = arr[queries[i][0]];
arr[queries[i][0]] = queries[i][1];
diff = arr[queries[i][0]] - temp;
for(j=1;j<=n;j++)
{
if(queries[i][0]>=Li[j] && queries[i][0]<=Ri[j])
F[j] = F[j] + diff;
++k;
}
}
else if(query_val[i] == 2)
{
++ans_count;
for(j=queries[i][0];j<=queries[i][1];j++)
ans[ans_count] = ans[ans_count] + F[j];
}
}
for(i=1;i<=ans_count;i++)
{
printf("%lld\n",ans[i]);
}
return 0;
}
虽然代码是正确的,但对于较大的测试用例需要大量时间。请帮助
【问题讨论】:
-
@j_random_hacker 怎么样?请帮忙解释一下
-
我真的无法比 Peter Fenwick 的原始论文更好地解释它,您可以从 Wikipedia 页面找到它,这是“Fenwick 树”的第一个 Google 结果。
-
听起来你已经有了正确的方法......每个元素最多只能出现在段树的
log N间隔中。 -
@arghbleargh 你能解释一下你是怎么得到它的吗?
-
@j_random_hacker 我知道 Fenwick 树,但你是如何在这里实现它们的?
标签: c++ algorithm time-complexity segment-tree