pushup操作:求一段区间的和,递归到儿子分别求
pushdown操作(懒标记/延迟标记):给一段区间加上一个数c,递归到每一个儿子去加
线段树函数
1、pushup(u)、pushdown()
2、build()
3、modify()修改
(1)单点修改
(2)区间修改pushdown
4、query()
区间划分
中点求解:mid = l + r >> 1
儿子:[l, mid],[mid + 1, r]
存储方式
利用堆的存储方式
对于编号是x的一个点
1、父节点是 x/2 (x >> 1)
2、左儿子是 2x (x << 1)
3、右儿子是 2x+1 (x << 1 | 1)
空间
假设倒数第二层有n个点
从第一层到倒数第二层有sum0 = 1*(1 - 2^n)/(1 - 2) = 2^n - 1 = 2n - 1个点(等比数列求和)
最后一层最多有2n个点,所以整棵树最多有4n-1个点
所以一般开4倍的空间即4n
建立函数build()
void build(int u,int l,int r)
{
tr[u] = {l,r};
if(l == r)return;
int mid = l + r >> 1;
build(u << 1,l,mid),build(u << 1 || 1,mid + 1, r);
}
例如:划分区间[0,1],mid = (l + r) / 2 = 0
递归到下一层(0,0),(1,1),这时候刚好发现他们相等,但是还没有加到线段树里面,
所以tr[u] = {l,r}要在if(l == r)前面
查询
假设我们要查询的区间是[l,r],树中区间是[Tl,Tr](也就是我们的划分区间)
1、[Tl,Tr]在[l,r]内部,直接返回
2、[Tl,Tr]与[l,r]有交集,如果和左边有交集递归左儿子,和右边有交集递归右儿子
3、不可能出现没有交集的情况
我们访问到的区间在logn级别内
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
struct node
{
int l,r;
int v;
}tr[N * 4];
void pushup(int u)//由子节点的信息来更新父节点
{
tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
if(tr[u].l == tr[u].r)return;
int mid = l + r >> 1;
build(u << 1, l, mid),build(u << 1 | 1, mid + 1, r);
}
int query(int u,int l,int r)//这里的l,r是我们询问的区间,u是根节点
{
//注意我们询问的区间一定会与树中区间有交集,所以不会有越界的情况
if(tr[u].l >= l && tr[u].r <= r)return tr[u].v;//如果说树中区间在询问区间的内部,那么直接返回
int v = 0;
int mid = tr[u].l + tr[u].r >> 1;
//注意我们此位置不需要考虑区间是否横跨mid,那并不影响得到答案
//而下一题则需要判断是否有交集,因为如果有交集则不能直接得到答案
if(l <= mid)v = query(u << 1, l, r);
if(r > mid)v = max(v, query(u << 1 | 1, l, r));
return v;
}
void modify(int u,int x,int v)//单点修改,将x位置的数改成v
{
if(tr[u].l == x && tr[u].r == x)tr[u].v = v;//如果找到叶子结点那么就直接改
else//否则递归找
{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid)modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);//每次更改后要同时更新父节点的值
}
}
int main()
{
int last = 0,n = 0;//分别表示上次询问的答案,加了多少个数
int m,p;cin >> m >> p;
build(1,1,m);//最会情况下最多加入m个点
while (m -- )
{
int x;char op[2];
scanf("%s%d",op,&x);
if(*op == 'Q')
{
last = query(1, n - x + 1, n);
cout << last << endl;
}
else
{
//第n + 1个位置没有被使用过,于是将他修改为题目中要求的值
modify(1,n + 1, ((LL)last + x) % p);
n ++;
}
}
return 0;
}