<= 求赞qwq,码字不易,你的支持是我写作的动力
宣传一下算法提高课题解合集
线段树模板题,顺手写一篇~
题目描述
给你一大串数字(编号为 $1$ 到 $N$,大小可不一定哦!),在你看过一遍之后,它便消失在你面前,随后问题就出现了,给你 $M$ 个询问,每次询问就给你两个数字 $A,B$,要求你瞬间就说出属于 $A$ 到 $B$ 这段区间内的最大数。
一天,一位美丽的姐姐从天上飞过,看到这个问题,感到很有意思(主要是据说那个宝藏里面藏着一种美容水,喝了可以让这美丽的姐姐更加迷人),于是她就竭尽全力想解决这个问题。
但是,她每次都以失败告终,因为这数字的个数是在太多了!
于是她请天才的你帮他解决。如果你帮她解决了这个问题,可是会得到很多甜头的哦!
输入格式
第一行一个整数 $N$ 表示数字的个数。
接下来一行为 $N$ 个数,表示数字序列。
第三行读入一个 $M$,表示你看完那串数后需要被提问的次数。
接下来 $M$ 行,每行都有两个整数 $A,B$。
输出格式
输出共 $M$ 行,每行输出一个数,表示对一个问题的回答。
数据范围
$1 \le N \le 2 \times 10^5$
$1 \le M \le 10^4$
$1 \le A \le B \le N$
RMQ 问题的解法有很多,线段树的解法虽然码量稍多,但很好想
这题就是不带修改询问区间最大值,只需维护区间询问即可
思路十分简单,重要内容我还是放注释里
首先是变量名介绍:
$tr$ 数组维护线段树
$x$ 数组是题目给的初始序列
$n$ 是初始序列的节点数 $m$ 是区间询问个数
然后是 $merge$ 操作和建树操作:
void merge(ll w)
//w 是当前节点的编号
{
tr[w].Max = max(tr[w << 1].Max, tr[w << 1 | 1].Max);//更新最大值
return;
}
void build(ll w, ll l, ll r)
//w 是当前节点的编号
//l 是当前节点的左端点
//r 是当前节点的右端点
{
tr[w].l = l, tr[w].r = r;//更新左右端点
if(l == r)
{
tr[w].Max = x[l];//直接赋值
return;
}
ll mid = (l + r) >> 1;//区间中点
build(w << 1, l, mid);//建左儿子
build(w << 1 | 1, mid + 1, r);//建右儿子
merge(w);//更新 w 节点
return;
}
接着是区间查询:
ll query(ll w, ll l, ll r)
//w 是当前节点的编号
//l 是查询区间的左端点
//r 是查询区间的右端点
{
if(l <= tr[w].l && tr[w].r <= r) return tr[w].Max;//如果在区间内,直接返回最大值
ll mid = (tr[w].l + tr[w].r) >> 1;//当前节点的区间中点
ll Max = -1e9;
if(l <= mid) Max = max(Max, query(w << 1, l, r));//递归左儿子
if(mid + 1 <= r) Max = max(Max, query(w << 1 | 1, l, r));//递归右儿子
return Max;//返回最大值
}
最后是主函数:
int main()
{
cin >> n;
for(int i = 1;i <= n;++ i) cin >> x[i];//输入初始节点的值
build(1, 1, n + 5);//建树
cin >> m;//询问个数
for(ll i = 1, l, r, w;i <= m;++ i)
{
//询问区间左右端点
cin >> l >> r;
cout << query(1, l, r) << endl;
}
return 0;
}
这题用int就能过吧,用不着long long