luogu 2253 好一个腰鼓

这是写过的第一道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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值