知识点 - 线段树
解决问题类型:
-
单点,区间更新
-
区间合并
- 最大子段和
-
扫描线
-
面积并
-
周长并
-
面积交
-
-
势能线段树
- 但某些修改存在一些奇妙的性质,使得序列每个元素被修改的次数有一个上限
可以在线段树每个节点上记录一个值,表示对应区间内是否每个元素都达到修改次数上限
区间修改时暴力递归到叶子节点,如果途中遇到一个节点,这个节点的对应区间内每个元素都达到修改次数上限则在这个节点 return 掉
- 但某些修改存在一些奇妙的性质,使得序列每个元素被修改的次数有一个上限
-
李超树 / 李超线段树 / 超哥线段树
- 维护一个二维平面
- 支持横坐标 [l,r] 范围内插入一条线段,查询某个横坐标上的最高
-
线段树维护单调子序列
- 给定一个序列,支持单点修改
- 询问给出 [ l , r ] [l,r] [l,r]
- 求有多少个 i ∈ [ l , r ] i∈[l,r] i∈[l,r] 满足 i = l i=l i=l 或者 [ l , i − 1 ] [l,i−1] [l,i−1] 内任何一个数都不大于第 i i i 个数
- 换句话说,求 [ l , r ] [l,r] [l,r] 内有多少个位置 i i i 是 [ l , r ] [l,r] [l,r] 内对应的以 i i i 为结尾的前缀最大值
-
线段树的扩展
复杂度:
建树
O
(
N
)
O(N)
O(N)
查找结点
O
(
l
o
g
N
)
O(logN)
O(logN)
例题
线段树合并
势能线段树,李超树,维护单调序列
代码
//POJ 3367 线段树 维护区间值
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <string>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <sstream>
#include <memory>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
#define max(a,b) ((a<b?b:a))
typedef long long ll;
typedef pair<int,int> pi;
const int MAXN = (int)5e4+7;
#define lson rt<<1
#define rson rt<<1|1
int sum[MAXN<<2];
int lsum[MAXN<<2];
int rsum[MAXN<<2];
int add[MAXN<<2];
inline void PushUp(int rt,int l,int r){
lsum[rt] = lsum[lson];rsum[rt] = rsum[rson];
if (lsum[lson] == (l+r)/2+1-l) {
lsum[rt] += lsum[rson];
}
if (rsum[rson] == r-(l+r)/2) {
rsum[rt] += rsum[lson];
}
sum[rt] = max(rsum[lson]+lsum[rson],max(sum[lson],sum[rson]));
}
inline void Build(int l,int r,int rt){
if (l == r){
sum[rt] = lsum[rt] = rsum[rt] = 1;
add[rt] = 0;
return ;
}
int m = (l+r)>>1;
Build(l,m,lson);
Build(m+1,r,rson);
sum[rt] = lsum[rt] = rsum[rt] = r-l+1;
add[rt] = 0;
}
inline void PushDown(int rt,int ln,int rn){
if (add[rt] == 1){ //全赋值为0,房间满了
sum[lson] = sum[rson] = 0;
lsum[lson] = rsum[lson] = lsum[rson] = rsum[rson] = 0;
add[lson] = add[rson] = 1;
add[rt] = 0;
}else if (add[rt] == 2) { // 全赋值为1,全是空房间的数量
sum[lson] = ln,sum[rson] = rn;
lsum[lson] = rsum[lson] = ln;
lsum[rson] = rsum[rson] = rn;
add[lson] = add[rson] = 2;
add[rt] = 0;
}
}
inline void Update(int L,int R,int C,int l,int r,int rt){
if (L <= l && r <= R){
if (C==2) {
lsum[rt] = rsum[rt] = sum[rt] = r-l+1;
add[rt] = 2;
}else {
lsum[rt] = rsum[rt] = sum[rt] = 0;
add[rt] = 1;
}
return ;
}
int m = (l+r)>>1;
PushDown(rt,m+1-l,r-m);
if (L <= m) Update(L,R,C,l,m,lson);
if (R > m) Update(L,R,C,m+1,r,rson);
PushUp(rt,l,r);
}
int Query(int l,int r,int rt,int len){
if (l == r){ //当查询为一到时候,可能会到最开始的地方。
return 1;
}
int m = (l+r)>>1;
PushDown(rt,m+1-l,r-m);
if (sum[lson] >= len) return Query(l,m,lson,len);
else if (rsum[lson]+lsum[rson] >= len){
return m-rsum[lson]+1;
}else {
return Query(m+1,r,rson,len);
}
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int N,Q;
cin >> N >> Q;
Build(1,N,1);
while (Q --) {
int op,x,y;
cin >> op;
if (op == 1) {
cin >> x;
if (sum[1] < x) cout << 0 << endl;
else {
int res = Query(1,N,1,x);
Update(res,x+res-1,1,1,N,1);
cout << res << endl;
}
}else {
cin >> x >> y;
Update(x,x+y-1,2,1,N,1);
}
}
}