一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
题目描述
C - Segment Tree, part 2 - Addition and First element at least X
There is an array of n elements, initially filled with zeros. You need to write a data structure that processes two types of queries:
add v to all elements on the segment from l to r−1, find the minimum index j such that j≥l and a[j]≥x. Input The first line contains two numbers n and m (1≤n,m≤100000), the size of the array and the number of operations. The following lines contain the description of the operations. The description of each operation is as follows:
1 l r v: add v to all elements on the segment from l to r−1 (0≤i<n, 0≤v≤10^4). 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 2 5 3
1 1 3 4
2 3 0
2 5 0
1 4 5 5
2 5 3
2 8 1
output
1
2
4
4
问题解析
题目是说给你一个初始为0的数组,进行两种操作,一个是把区间l~r的数都加上v,一种是让你找到下标大于等于j,且值大于等于x的第一个节点的位置。
区间加就是很基础的标记下传,这里主要讲第二种操作,线段树节点记录的是区间内的最大值,每次我们判断一下,如果左子节点的值大于x,那我们优先去左边找,如果找到的节点下标小于j,那我们就返回一个-1,然后回到上一层去右边找,也就是说我们这个线段树要有个回溯功能,如果再去右边找,找到的节点是小于x或是下标小于j的,那也返回-1。
但是这种做法,最坏情况下我们一次查询可能要把整个线段树跑一边,相当慢了,所以我们要减枝。当当前区间的最大值是小于x时,我们也不用纠结去左或右了,直接返回-1即可。或者当前区间的下标都是小于j的,我们也可直接返回-1。
#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<ll, ll>PII;
const int N = 100500;
ll f[4 * N], b[4 * N], lmax[4 * N], rmax[4 * N], date[4 * N];
void push_down(ll k, ll l, ll r)
{
if (b[k] != 0)
{
int m = (l + r) / 2;
b[k + k] += b[k];
b[k + k + 1] += b[k];
f[k + k] += b[k];
f[k + k + 1] += b[k];
b[k] = 0;
}
}
void revise(ll k, ll l, ll r, ll x, ll y, ll v)
{
if (l == x && r == y)
{
b[k] += v;
f[k] += v;
return;
}
push_down(k, l, r);
int m = (l + r) / 2;
if (y <= m)revise(k + k, l, m, x, y, v);
else
if (x > m)revise(k + k + 1, m + 1, r, x, y, v);
else
{
revise(k + k, l, m, x, m, v);
revise(k + k + 1, m + 1, r, m + 1, y, v);
}
f[k] = max(f[k + k], f[k + k + 1]);
}
ll calc(int k, int l, int r, int x, int y)
{
if (l == r)
{
if (l < y || f[k] < x)return -1;
return l;
}
push_down(k, l, r);
int m = (l + r) / 2;
if (f[k] < x || r < y)return -1;
int res = -1;
if (f[k + k] >= x)res = calc(k + k, l, m, x, y);
if (res == -1)res = calc(k + k + 1, m + 1, r, x, y);
f[k] = max(f[k + k], f[k + k + 1]);
return res;
}
int main()
{
int n, m;
cin >> n >> m;
while (m--)
{
ll t, x, y, v;
cin >> t >> x >> y;
if (t == 1)
{
cin >> v;
revise(1, 1, n, x + 1, y, v);
}
else cout << max((ll)-1, calc(1, 1, n, x, y + 1) - 1) << endl;
}
return 0;
}