特性神教相关算法类介绍

这篇文章主要介绍了符合特性神教教规的一些常见语法类。

前缀和

成员

前缀和非常简单,只需要一个数组即可解决。因此,其成员只有一个 vector

前缀和是一个离线算法,在构造之后可以 查询单个元素或者区间元素和。因此,我提供了 get_sumget_val 两个方法,分别用来获取区间和与单个成员。为了方便,我还重载了 operator[],方便获取单个元素。为了使代码美观,对于 get_valoperator[],其返回值与 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();}
};
posted @ 2023-07-07 17:28  MrPython  阅读(5)  评论(0)    收藏  举报  来源