特性神教相关算法类介绍
这篇文章主要介绍了符合特性神教教规的一些常见语法类。
前缀和
成员
前缀和非常简单,只需要一个数组即可解决。因此,其成员只有一个 vector
。
前缀和是一个离线算法,在构造之后可以 查询单个元素或者区间元素和。因此,我提供了 get_sum
与 get_val
两个方法,分别用来获取区间和与单个成员。为了方便,我还重载了 operator[]
,方便获取单个元素。为了使代码美观,对于 get_val
与 operator[]
,其返回值与 get_sum
相同,均为 int
。
由于前缀和的单点修改是 的,及其不常用,因此我没有提供修改的方法。
在部分场合我们可能需要遍历整个前缀和。不过编写迭代器较复杂,仍然需要大家使用下标遍历。
class PrefixSum{
vector<int> sum;
public:
PrefixSum(vector<int> const&);
int get_sum(size_t,size_t);
int get_val(size_t);
int operator[](size_t);
size_t size(void){return sum.size();}
};
构造
由于从 开始存储,前缀和的相关机制也应该作出修改。
虽然特性神教提倡从 开始存储数据,但在前缀和中前面留空一个 仍是个明智的主意,因此我仍然选择从 存储。
我们定义前缀和数组 为:
递归定义,即
s_i=\left\{\begin{aligned} &0,&i=0\\ &s_{i-1}+a_\color{red}{i-1},&i\ne0 \end{aligned}\right.与一般前缀和不同的是,由于 的下标向前挪动了一位(起始点从 到了 ),所以在计算时也需要前挪一位取值(即标红部分)。
vector
初始化时会自动赋初值 ,因此第一行可以不用单独写,直接从 循环即可。
PrefixSum::PrefixSum(vector<int> const& a){
sum.resize(a.size()+1); // 注意!从 1 开始存储,最后结束的位置也 +1,因此需要将数组大小 +1。
for (size_t i=1;i<=a.size();++i) sum[i]=sum[i-1]+a[i-1];
}
区间求和
如何求区间 (仍然遵循左闭右开,即 )的区间元素和?
前缀和原定义为 ,左闭右开表示为 。既然我们将 数组从 存储,那么将原下标 即可达到我们想要的效果。即:
int PrefixSum::get_sum(size_t m,size_t n){
return sum[n]-sum[m];
}
单点求和
单点求和,也就是求区间 的值。直接调用写好的方法。同时,由于 operator[]
的意义与 get_val
相同,也可以直接调用。不必担心效率问题,在 O2
优化下这些函数都可以被展开。
int PrefixSum::get_val(size_t m){
return get_sum(m,m+1);
}
int PrefixSum::operator[](size_t m){
return get_val(m);
}
全部代码
class PrefixSum{
vector<int> sum;
public:
PrefixSum(vector<int> const& a){
sum.resize(a.size()+1); // 注意!从 1 开始存储,最后结束的位置也 +1,因此需要将数组大小 +1。
for (size_t i=1;i<=a.size();++i) sum[i]=sum[i-1]+a[i-1];
}
int get_sum(size_t m,size_t n){
return sum[n]-sum[m];
}
int get_val(size_t m){
return get_sum(m,m+1);
}
int operator[](size_t m){
return get_val(m);
}
size_t size(void){return sum.size();}
};