795前缀和,线段树,树状数组
题目描述
输入一个长度为
接下来再输入
对于每个询问,输出原序列中从第
输入格式
第一行包含两个整数
第二行包含
接下来
输出格式
共
数据范围
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
算法
区间和这里提供三种做法:前缀和,线段树,树状数组
前缀和
思路与推导
所谓前缀和,就是我们开一个
其中
如果我们要查询
因为
所以
我们已经定义了
将
我们这道题目只需用
怎么预处理呢
所以我们只要确定了
后面的部分循环即可
时间复杂度
代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, a[N], m, s[N];
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;i ++ )
scanf("%d", &a[i]);
//预处理s数组
s[1] = a[1];
for (int i = 2; i <= n; i ++ )
s[i] = s[i - 1] + a[i];
int l, r;
while (m -- )
{
scanf("%d%d", &l, &r);
printf("%d\n",s[r] - s[l - 1]);
}
return 0;
}
线段树
思路
这里就不具体说线段树了,这里要维护的区间问题是区间和,不需要修改,故线段树也很合适,开的结构体里面放
时间复杂度
代码
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n, m, a[N];
struct Seg
{
int l, r, sum;
} tr[N << 2]; //线段树要开四倍空间
void build(int p, int l, int r) //代表编号为p的节点管理[l, r]这个区间
{
Seg& cur = tr[p];
cur.l = l, cur.r = r;
if (l == r) return void(cur.sum = a[l]); //叶子节点直接赋值
int mid = cur.l + cur.r >> 1; //取中间点
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r); //左右建子树
cur.sum = tr[p << 1].sum + tr[p << 1 | 1].sum; // pushup操作
}
int query(int p, int l, int r)
{
Seg& cur = tr[p];
if (cur.l >= l && cur.r <= r) return cur.sum; // 完全包含直接返回
int mid = cur.l + cur.r >> 1, s = 0;
if (l <= mid) s += query(p << 1, l, r); //左子包含递归左子
if (r > mid) s += query(p << 1 | 1, l, r); // 右字包含递归右子
return s;
}
int main()
{
scanf("%d%d", &n, &m);
//读入并建树
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]);
build(1, 1, n);
while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(1, l, r));
}
return 0;
}
树状数组
思路
树状数组本来就可以维护前缀
所以这里用树状数组
这里提供两种写法,一种是普通BIT,一种是面向对象泛型BIT
时间复杂度
比线段树快
代码
普通写法
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int tr[N], n, Q, a[N];
void add(int x, int v)
{
for (; x <= n; x += x & -x) tr[x] += v;
}
int query(int x)
{
int res = 0;
for (; x; x -= x & -x) res += tr[x];
return res;
}
int main()
{
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]), add(i, a[i]);
while (Q -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(r) - query(l - 1));
}
return 0;
}
面向对象
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, Q, a[N];
template<typename T>
struct Bit
{
T c[N];
void add(T x, const T v)
{
for (; x <= n; x += x & -x) c[x] += v;
}
T query(T x)
{
T res = 0;
for (; x; x -= x & -x) res += c[x];
return res;
}
} ; Bit<int> tr;
int main()
{
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]), tr.add(i, a[i]);
while (Q -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", tr.query(r) - tr.query(l - 1));
}
return 0;
}
时间对比
撇去评测机的微小影响
速度排名是:前缀和,树状数组(1),树状数组(2),线段树
End
写线段树和BIT有一点大炮打蚊子的感觉,但也不失为一次练习
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业