传送门:线段树也不过如此
这标准线段树模板题,但是多年没碰线段树,现在已经基本不会写了T^T
首先是创建线段树。
线段树是个满二叉树,如果从长度为n的数组上生成线段树,那么树节点个数m满足
m ≤ 2 q + 1 − 1 m \le 2^{q+1} - 1 m≤2q+1−1
其中
2
q
>
n
∧
2
q
−
1
<
n
2^q > n \land 2^{q-1} < n
2q>n∧2q−1<n
此外对于满二叉树来说,假设当前节点编号为n,则left node编号为
2
n
2n
2n,right node编号为
2
n
+
1
2n+1
2n+1,ancestor编号为
n
/
2
n/2
n/2。借助这些性质可以实现在数据中存储树节点时快速访问。
应用到这个题来说,需要建两棵线段树:一颗记录行,一颗记录列。根据题意,初始状态下没有rock,所以叶结点val都是0
然后是线段树更新:首先到叶结点,更新叶结点数据;然后,回溯的时候更新ancestor中记录的区间的值。
对于这个题来说,行线段树中非叶结点中记录的是从left行到right行中,一行最少有几个rock。回溯时根据left和right节点的值更新区间最小值。列线段树同理。
最后是查询。首先要明确的一点是,查询的区间 x x x始终是数组的子区间 x ⊂ [ 1 , n ] x\subset [1,n] x⊂[1,n],所以查询的时候只考虑Ⅰ情况即可
具体到每个非叶结点所代表的区间查询时,借助中间变量mid来判断下一步需要查询的区间。设需要查询的区间为
[
l
,
r
]
[l,r]
[l,r],非叶结点代表区间
[
L
,
R
]
[L,R]
[L,R],非叶节点代表区间的区间中点为
m
i
d
mid
mid
如果
m
i
d
<
l
mid < l
mid<l,说明需要去区间
[
m
i
d
,
R
]
[mid, R]
[mid,R]进一步查询,故go to right;
如果
r
<
m
i
d
r < mid
r<mid,说明需要去区间
[
L
,
m
i
d
]
[L,mid]
[L,mid]进一步查询, 故go to left;
如果
l
<
m
i
d
∧
m
i
d
<
r
l<mid \land mid<r
l<mid∧mid<r,则说明需要在left 和 right两个子节点都进行查询。
如果
l
≤
L
∧
R
≤
r
l\le L \land R\le r
l≤L∧R≤r,也就是非叶节点代表区间x是待查询区间的子区间时,停止查询,返回该区间x的val。
其实线段树查询的时候,不断将查询区间细分,分为线段树中记录的区间,再见这些区间合并,最终得出结果。
具体到这个题,需要查询的是区间最小值,也就是所有子区间的最小值。
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
template<class T> void _deb(const char *name,T val){
cout<<name<<val<<endl;
}
const int MAXN = 1e5+5;
struct tree{
int left,right;
int attacked;
};
int n,q;
tree row[MAXN*4],col[MAXN*4];
int creat(int pos,int l,int r, tree* T);
void update(int pos,bool, tree*);
int query(int l,int r,int pos, tree*);
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>n>>q;
creat(1,1,n, row);
creat(1,1,n, col);
while(q--) {
int sym;
cin>>sym;
int commonA,commonB;
switch(sym) {
case 1:
// insert
cin>>commonA>>commonB;
update(commonA,true,row);
update(commonB,true, col);
break;
case 2:
// remove
cin>>commonA>>commonB;
update(commonA,false,row);
update(commonB,false,col);
break;
case 3:
// query
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
int ans = max(query(x1,x2,1,row), query(y1,y2,1,col));
if(ans>0){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
break;
}
}
re 0;
}
int creat(int pos,int l,int r, tree* T) {
T[pos].left = l,T[pos].right = r;
if(r == l) {
return T[pos].attacked = 0;
}
int mid = l + (r-l)/2;
int left_ans = creat(pos<<1,l,mid,T);
int right_ans = creat(pos<<1|1, mid+1, r,T);
return T[pos].attacked = 0;
}
void update(int pos,bool sta, tree* T){
int inf = T[1].left;
int sup = T[1].right;
int nowPos = 1;
while(inf<sup) {
int mid = inf+(sup-inf)/2;
if(pos>mid) {
nowPos = nowPos<<1|1;
inf = T[nowPos].left;
}else{
nowPos = nowPos<<1;
sup = T[nowPos].right;
}
}
T[nowPos].attacked = sta? T[nowPos].attacked+1 : T[nowPos].attacked-1;
while(nowPos!=1) {
nowPos = nowPos>>1;
T[nowPos].attacked = min(T[nowPos<<1|1].attacked ,T[nowPos<<1].attacked);
}
}
int query(int l,int r,int pos, tree* T){
if(l<=T[pos].left && T[pos].right <= r){
return T[pos].attacked;
}
int mid = T[pos].left + (T[pos].right - T[pos].left)/2;
int ans = INT_MAX;
if(r>mid){
ans = min(ans, query(l,r,pos<<1|1, T));
}
if(l<=mid) {
ans = min(ans,query(l,r,pos<<1, T));
}
return ans;
}