【点修改 动态RMQ】 最大数
传送门
题意
维护一个序列\(a_{1},a_{2},\dots,a_{n}\),序列中的每个数字在\(0 \sim p-1\)之间,初始为空,
\(m\)个操作,操作一共有两种:
- \((Q,L)\)表示查询序列后\(L\)个数的最大值
- \((A,t)\)表示在序列后加一个数长度变为\(n+1\)
其中每次加的数是\((t+a)\, mod \, p\),\(a\)是上一次查询的值,如果还未进行查询,则\(a=0\),对于每个查询单独输出一行
数据范围
\(1 \leq m \leq 2 \times 10^{5}\)
\(1 \leq p \leq 2\times 10^{9}\)
\(0 \leq t < p\)
题解
线段树只需要维护当前区间内的\(RMQ\)
- 因为初始无数据,根据操作在序列后端添加数据,所以初始化一个和操作个数相同大小的线段树即可
- 动态增加点,每次修改都从根开始向下查找要修改的叶子节点然后递归更新
- 每次查询如果当前的区间是查询区间的子区间就直接返回已经得到的当前区间的最值
- 否则求出来当前区间的中点左右递归满足查询区间的部分
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node
{
int l, r;
int mx;
#define l(u) u * 2
#define r(u) u * 2 + 1
} tr[4 * N];
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;
build(l(u), l, mid);
build(r(u), mid + 1, r);
}
void push_up(int u) //从低向上
{
tr[u].mx = max(tr[l(u)].mx, tr[r(u)].mx);
}
void modify(int u, int id, int x) //单点修改将序列中a[id]改为值x
{
if(tr[u].l == id && tr[u].r == id)
tr[u].mx = x;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(id <= mid) modify(l(u), id, x);
else modify(r(u), id, x);
push_up(u);
}
}
int query(int u, int l, int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].mx;
int mid = tr[u].l + tr[u].r >> 1;
int v = 0;
if(l <= mid) v = query(l(u), l, r);
if(r > mid) v = max(v,query(r(u), l, r));
return v;
}
int m, p;
int main()
{
scanf("%d%d", &m, &p);
int n = 0, last = 0;
build(1, 1, m);
int x; char op[2];
while(m --)
{
scanf("%s%d", op, &x);
if(*op == 'Q')
{
last = query(1, n - x + 1, n);
printf("%d\n", last);
}
else
modify(1, n + 1, (x + last) % p), n ++;
}
}

浙公网安备 33010602011771号