一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
题目描述
这是codeforces的线段树专题。
D - Segment Tree, part 1 - Codeforces
In this task, you need to add to the segment tree the operation of finding for the given xx and ll the minimum index j such that j≥l and a[j]≥x.
Input
The first line contains two integers n and mm (1≤n,m≤1000001≤n,m≤100000), the size of the array and the number of operations. The next line contains n numbers ai, the initial state of the array (0≤ai≤10^9). The following lines contain the description of the operations. The description of each operation is as follows:
- 1 i v: change the item with index i to vv (0≤i<n, 0≤v≤10^9).
- 2 x l: find the minimum index j such that j≥l and a[j]≥x (0≤x≤10^9, 0≤l<n). If there is no such element, print −1. Indices start from 0.
Output
For each operation of the second type, print the answer for the query.
Example
input
5 7
1 3 2 4 3
2 3 0
2 3 2
1 2 5
2 4 1
2 5 4
1 3 7
2 6 1
output
1
3
2
-1
3
问题解析
这题意思是说,给你一个数组,有两种操作:
第一种:把下标为i的元素改为y。
第二种:找到第一个大于等于下标x,且元素大于等于y的元素。如果没有,输出-1
构造一个线段树,每个节点f[k]的意思是:当前区间内的最大值。这样我们就可以根据左右子节点的情况来判断我们要去左边还是右边了。当左节点大于y时,我们去左边找,反之去右边找,如果没有大于等于y的元素,就返回-1。
但是!这样是有问题的,就算左边的节点大于y,但是有可能这个大于y的节点的下标是小于x的,这样显然不合规矩。此时你说,那我能不能同时记录再记录一下这个最大值的下标,这样也会有问题,比如2 4 3 5 1这个数组,我们要找下标大于等于1,元素大于等于2的元素,此时左边的最大值是4,下标是2,你一看,“哦这样不行我去右边找吧”,然后你找到了第三个元素,但答案是第一个元素。
这问题的解决方法就是我们应该造一个特殊的线段树,能够回溯的线段树,这样我们被“骗”去左边后还能再有次机会去右边找。具体该怎么做?
我们可以每次准备一个变量res接受结果,如果左边的指小于目标值,自然就不用去左边找了,但如果左边值大于目标值,我们就要去左边找,然后用res接收返回的结果,当我们找到叶节点后,发现这个节点不满足条件,我们被“骗”了,就返回一个-1回去。当res接收到-1时,我们就知道被骗了,再去右边找。如果没被骗,就返回下标给res。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100050;
int f[4 * N], a[N];
void buildtree(int k,int l,int r)
{
if (l == r)
{
f[k] = a[l];
return;
}
int m = (l + r) / 2;
buildtree(k + k, l, m);
buildtree(k + k + 1, m+1, r);
f[k] = max(f[k + k], f[k + k + 1]);
}
void revise(int k, int l, int r, int x, int y)
{
if (l == r)
{
f[k] = y;
return;
}
int m = (l + r) / 2;
if (x <= m)revise(k + k, l, m, x, y);
else revise(k + k + 1, m + 1, r, x, y);
f[k] = max(f[k + k] , f[k + k + 1]);
}
int quire(int k, int l, int r, int x, int y)
{
if (l == r)
{
if (f[k] >= y && l >= x)return l;
else return -1;
}
int m = (l + r) / 2;
int res = -1;
if (f[k + k] >= y && m >= x) res = quire(k + k, l, m, x, y);
if (res == -1) res = quire(k + k + 1, m + 1, r, x, y);
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> a[i];
buildtree(1, 1, n);
while (m--)
{
int st, x ,y;
cin >> st;
if (st == 1)
{
cin >> x >> y;
revise(1, 1, n, x + 1, y);
}
else
{
cin >> y >> x;
int res = quire(1, 1, n, x+1,y);
if (res != -1)res--;
cout << res << endl;
}
}
return 0;
}