线段树详解
https://blog.csdn.net/sjystone/article/details/115398593
https://www.acwing.com/problem/content/1277/
给定序列
1.向序列末尾添加一个数
2.询问序列后面l个数的最大值
先将位置开好[4N],用变量n表示当前位置,可将操作转化为
操作1 -> 在某个位置修改一个数(可以用RMQ倍增方法做)
操作2 -> 求[n-l+1, n]内的最大值,也就是变成询问区间最大值
每一个点,需要记录左右端点,最大值
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int n, m, p, last;
struct node{
int l, r, v;
} tr[4 * N]; //最多有4N个节点
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 = l, tr[u].r = r; //建立新节点
if ( l == r ) return; //如果是叶节点则返回
int mid = l + r >> 1; //分为[l,mid] [mid+1,r]
build(u << 1, l, mid); //递归建立左儿子
build(u << 1 | 1, mid + 1, r);//递归建立右儿子
}
int query(int u, int l, int r) //查询
{
int v = 0; //最大值
if ( tr[u].l >= l && tr[u].r <= r ) return tr[u].v; //若访问的区间在要求区间内
else
{
int mid = tr[u].l + tr[u].r >> 1;
if ( mid >= l ) v = query(u << 1, l, r); //中点在l右边则递归左儿子
if ( mid < r ) v = max(v, query(u << 1 | 1 ,l ,r)); //不取等,因为右儿子是从mid+1开始
}
return v;
}
void modify(int u, int x, int v) //修改
{
if ( tr[u].l == tr[u].r ) tr[u].v = v; //若是叶节点则赋值
else
{
int mid = tr[u].r + tr[u].l >> 1;
if ( mid >= x ) modify(u << 1, x, v); //同上,区间变成了点
else modify(u << 1 | 1, x, v);
pushup(u); //回溯时通过子节点,更新父节点的最大值
}
}
int main()
{
cin >> m >> p;
build(1, 1, m); //从1号节点开始,建立区间为1到m的线段树
while ( m -- )
{
int x;
char op[2];
scanf("%s%d", op, &x);
if ( *op == 'Q' )
{
last = query(1, n - x + 1, n); //查询区间[n-x+1,n]的最大值
printf("%d\n", last);
}
else
{
modify(1, n + 1, (x + last) % p); //在n+1的位置插入值
n ++; //总数加1
}
}
return 0;
}