题目链接
我们先规定L为0 R为1,题目就是求最长的01/10串
从题意来看,类似单点修改 区间查询,于是思考能否用线段树来维护某些东西,从而在logn时间内完成查询。
我们令tree[rt].ans 表示rt对应区间的答案,也就是最长的01/10串
我们考虑左右两个子区间 [l,mid] 、[mid+1,r] 的合并的若干种情况:
①当左子区间右端点等于右子区间左端点 :此时根节点答案只能是左右子树答案中的最大值。
tree[rt].ans=max( tree[lc(rt)].ans , tree[rc(rt)].ans )
(到这里我们线段树需要维护的信息有:1、区间答案 2、左右端点的值)
②当左子区间右端点不等于右子区间左端点:此时此时根节点答案是左右子树答案、合并后中间段产生的答案三者中的最大值。
那么问题来了,合并后中间段的答案怎么求呢? 其实就等于左子树后缀答案加上右子树前缀答案 。
那么到这里线段树这需要维护的信息还要加上区间前缀中最长的01/10串的长度以及后缀中最长的01/10串的长度
但是pushup过程中前缀答案该怎么维护呢?也分两种情况
①:当左子树的前缀长度小于左子区间,那么rt的前缀答案直接继承左子树的前缀答案
②:当左子树的前缀长度等于左子区间,也就是左子区间整个就是01/10串,此时如果右子区间左端点等于左子区间右端点,那么rt的前缀也是直接继承左子树的前缀答案, 否则还要加上右子树前缀答案。
可能有点绕,看代码和注释就很明显啦
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5+7;
const ll inf = 34359738370;
//给定01串,q次修改 每次翻转一个位置。 每次修改要输出此时最长的不含连续元素的子串长度(01串/10串的长度)
//线段树维护以下信息:
//1、区间最长的01/10串长度 2、区间左右端点 3、前缀中最长答案 4、后缀中最长答案
struct node
{
int ans;//区间答案
int pans,sans,lv,rv;//前缀中最长答案 后缀最长答案 左端点 右端点
}tree[maxn<<2];
int n,m;
inline int lc(int &rt)
{
return rt<<1;
}
inline int rc(int &rt)
{
return rt<<1 | 1;
}
inline void pushup(int &rt,int l,int r)
{
int mid=