这是写过的第一道pushup最难写的线段树
记录的几个变量 :
lv :区间最左边的值
rv:区间最右边的值
fl fr :区间从左(右)边开始可以延伸的最长的答案
fm :区间中间部分的最长的答案
单点修改的操作很简单 关键是区间合并
在合并的时候,最主要的是对大区间fm的修改 这是第一点
第二点是 如果两个区间合并时又有了新的答案(此处与fv,rv有关),那么需要进行一些比较复杂的操作
#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<queue>
#include<string>
#define LL long long
#define RE register
using namespace std ;
inline LL read() {
LL x = 0;bool flag = true ;
char k = getchar();
while((k > '9' || k < '0') && k != '-') k = getchar() ;
while(k == '-') {flag = false ;k = getchar();}
while(k >= '0' && k <= '9') {x = x * 10 + k - '0';k = getchar() ;}
return flag ? x : -x;
}
const int maxn = 20005;
int n,m,t = 1;
struct TRee{
int lc,rc,lv,rv;
int fl,fr,fm,siz;
}a[maxn << 1];
void pushup(int u) {
a[u].lv = a[a[u].lc].lv ;
a[u].rv = a[a[u].rc].rv ;
a[u].fl = a[a[u].lc].fl ;
a[u].fr = a[a[u].rc].fr ;
a[u].fm = max(a[a[u].lc].fr,a[a[u].rc].fl) ;
a[u].fm = max(a[u].fm,max(a[a[u].lc].fm,a[a[u].rc].fm)) ;
if(a[a[u].lc].rv != a[a[u].rc].lv) {
a[u].fm = max(a[u].fm,a[a[u].lc].fr + a[a[u].rc].fl) ;
if(a[a[u].lc].fl == a[a[u].lc].siz) a[u].fl += a[a[u].rc].fl ;
if(a[a[u].rc].fr == a[a[u].rc].siz) a[u].fr += a[a[u].lc].fr ;
}
return ;
}
void build(int u,int l,int r) {
a[u].siz = r - l + 1;
if(l == r) {a[u].lv = a[u].rv = 0;a[u].fl = a[u].fm = a[u].fr = 1;return ;}
int mid = (l + r) >> 1;
a[u].lc = ++t;build(a[u].lc,l,mid) ;
a[u].rc = ++t;build(a[u].rc,mid+1,r) ;
pushup(u) ;
}
void update(int u,int l,int r,int x) {
if(l == x && r == x) {a[u].lv = a[u].rv = 1 - a[u].lv;return ;}
int mid = (l + r) >> 1;
if(x <= mid) update(a[u].lc,l,mid,x) ;
else update(a[u].rc,mid+1,r,x) ;
pushup(u) ;
}
int main() {
scanf("%d%d",&n,&m) ;
build(1,1,n) ;
for(RE int i = 1;i <= m ;++i) {
int x = read() ;
update(1,1,n,x) ;
printf("%d\n",max(a[1].fl,max(a[1].fr,a[1].fm))) ;
}
return 0;
}
博客记录了线段树相关内容,涉及记录的变量如区间左右值、左右延伸最长答案、中间部分最长答案等。指出单点修改操作简单,关键在于区间合并,合并时主要是修改大区间fm,若合并有新答案还需进行复杂操作。
1万+

被折叠的 条评论
为什么被折叠?



