「Luogu P2253 好一个一中腰鼓!」
就这道题的理论难度来说绿题是有点低了,但是这道题的实际难度来看,顶多黄题,所以建议加强数据或出数据升级版.
前置芝士
具体做法
暴力的方法想必都会,所以来讲一下正解.看到标签是线段树,所以正解就是线段树了(毫无问题的逻辑).这颗线段树上的节点如果只记录区间中的最长的符合条件的区间长度想必是无法转移的,所以需要记录一下的量:
- 当前区间最左端开始的最长的符合条件的区间的长度
- 当前区间最右端开始的最长的符合条件的区间的长度
- 当前区间中最长的符合条件的区间的长度
- 当前区间的最左边的数
- 当前区间的最右边的数
- 当前区间的长度
然后就可以合并了.
下面是几个合并时需要注意的地方:
- 整棵树的最左端的值就是左子树的最左端的值(右边同理)
- 整棵树的中间的最长分符合区间为左右子树的最长区间中的大值,但如果左右区间在相连处的数字不同,那么左子树的右边可以和右子树的左边相连,所以还有和这两个数相加的数去一个max
- 整颗子树的如果从头到尾都符合时需要特别处理:当左右子树可以相连时整颗树的左右最大值不能直接从单一一棵子树中获得,而需要一棵完整的子树加上另一棵子树的一部分(也可能是全部)
如:
左子树:1 0 1
右子树 0 0 1
如果没有这个特判时那么整颗树的左边最长为3
但是如果相连后1 0 1 0 0 1,左边最长为4
代码
#include<bits/stdc++.h>
#define rap(i,first,last) for(int i=first;i<=last;++i)
#define Lson now*2
#define Rson now*2+1
#define Middle (left+right)/2
#define Left Lson,left,Middle
#define Right Rson,Middle+1,right
using namespace std;
const int maxN=1e5+7;
struct Tree//保存整棵树
{
int lmax,rmax,mmax,l,r,len;//如上所示的需要保留的值
}tree[maxN*4];
int N,M;
void PushUp(int now)
{
tree[now].lmax=tree[Lson].lmax;//先直接将子树的左右最长赋值到整颗数中
tree[now].rmax=tree[Rson].rmax;
tree[now].l=tree[Lson].l;//整颗树的左右值
tree[now].r=tree[Rson].r;
tree[now].mmax=max(tree[Lson].mmax,tree[Rson].mmax);//先从左右子树中中间的最长符合区间中取一个最大的
if(tree[Lson].r!=tree[Rson].l)//当左右可以相连时
{
tree[now].mmax=max(tree[now].mmax,tree[Lson].rmax+tree[Rson].lmax);//需要再取一个大值
if(tree[Lson].mmax==tree[Lson].len)//如上注意3
{
tree[now].lmax=tree[Rson].lmax+tree[Lson].len;
}
if(tree[Rson].mmax==tree[Rson].len)
{
tree[now].rmax=tree[Lson].rmax+tree[Rson].len;
}
}
}
void Build(int now=1,int left=1,int right=N)//建树,不多说
{
tree[now].len=right-left+1;
if(left==right)
{
tree[now].lmax=tree[now].rmax=1;
tree[now].mmax=1;
tree[now].l=tree[now].r=1;
return;
}
Build(Left);
Build(Right);
PushUp(now);
}
void UpData(int change,int now=1,int left=1,int right=N)//修改,因为只有单点修改所以也用不上懒标记,直接将覆盖需要修改位置的所有区间都修改就好了
{
if(left>change||right<change)return;
if(left==right)//到叶节点时
{
tree[now].l^=1;
tree[now].r^=1;
return;
}
UpData(change,Left);
UpData(change,Right);
PushUp(now);//合并
}
int Query()
{
return max(tree[1].mmax,max(tree[1].lmax,tree[1].rmax));//全局查找,直接在整颗树中找一个最长的附和条件时区间
}
int main()
{
scanf("%d%d",&N,&M);
Build();//建树
int change;
rap(i,1,M)
{
scanf("%d",&change);
UpData(change);//修改
printf("%d\n",Query());//查询
}
return 0;
}