洛谷题单指南-线段树-P6492 [COCI2010-2011#6] STEP
原题链接:https://www.luogu.com.cn/problem/P6492
题意解读:一个序列,初始L,可以指定一个位置修改,L修改成R,R修改成L,可以令L=0,R=1,然后每次修改后输出序列最长不连续0、1(0/1交替出现)的长度。
解题思路:序列支持单点修改(0->1,1->0),区间查询(最长不连续0、1长度),因此可以采用线段树,不需要懒标记。
关键在于找到线段树节点需要维护哪些信息。
比较直观的,节点需要维护区间内不连续0、1的最大长度,设为len
合并逻辑可能有三种情况:
所以,线段树节点需要维护的信息如下:
1、区间内不连续0、1的最大长度len
父节点len = max(左子结点len, 右子节点len, 左子结点sublen + 右子节点prelen)
2、区间内不连续0、1的最大前缀长度prelen
父节点prelen = max(左子结点prelen, 左子结点长度 + 右子节点prelen)
3、区间内不连续0、1的最大后缀长度sublen
父节点sublen = max(右子节点sublen, 右子节点长度 + 左子结点sublen)
4、区间左端点值left
父节点left = 左子结点left
5、区间右端点值right
父节点right = 右子节点right
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct Node
{
int l, r;
int len, prelen, sublen;
int left, right;
} tr[N * 4];
int n, m;
void pushup(int u)
{
Node &root = tr[u];
Node &lchild = tr[u << 1];
Node &rchild = tr[u << 1 | 1];
root.len = max(lchild.len, rchild.len);
if(lchild.right != rchild.left)
root.len = max(root.len, lchild.sublen + rchild.prelen);
root.prelen = lchild.prelen;
if(lchild.len == lchild.r - lchild.l + 1 && lchild.right != rchild.left)
root.prelen = max(root.prelen, lchild.r - lchild.l + 1 + rchild.prelen);
root.sublen = rchild.sublen;
if(rchild.len == rchild.r - rchild.l + 1 && lchild.right != rchild.left)
root.sublen = max(root.sublen, rchild.r - rchild.l + 1 + lchild.sublen);
root.left = lchild.left;
root.right = rchild.right;
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r)
{
tr[u].len = tr[u].prelen = tr[u].sublen = 1;
tr[u].left = tr[u].right = 0; //初始都是0
}
else
{
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void update(int u, int x)
{
if(tr[u].l == tr[u].r)
{
tr[u].left = tr[u].right = 1 ^ tr[u].left; //0变1,1变0
}
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) update(u << 1, x);
else update(u << 1 | 1, x);
pushup(u);
}
}
int main()
{
cin >> n >> m;
build(1, 1, n);
int x;
while(m--)
{
cin >> x;
update(1, x);
cout << tr[1].len << endl;
}
return 0;
}