题解 CF145E Lucky Queries

线段树。

在树中我们维护四个东西:最长不下降子序列的长度,最长不上升子序列的长度,\(4\)的个数和\(7\)的个数。

线段树最重要的是\(push\,up\),也就是合并。

显然\(4\)的个数和\(7\)的个数就直接加一下,没什么好说的。

因为数列中不是\(4\)就是\(7\),所以这个最长不下降子序列一定有一个\(4\)\(7\)的分界点。

如果这个分界点在左儿子里,那么父节点的最长不下降子序列的长度就是左儿子的最长不下降子序列长度加上右儿子的\(7\)的个数。

如果这个分界点在右儿子里,那么父节点的最长不下降子序列的长度就是左儿子的\(4\)的个数加上右儿子的最长不下降子序列长度。

最长不上升子序列的长度同理。

这部分的代码

struct node
{
	ll l,r,mid,vals,valx,num4,num7;//vals是最长不上升子序列长度,valx是最长不下降子序列长度
	bool tag;//lazy标记
}seg[N<<2];
inline void pushup(re ll pos)
{
	seg[pos].vals=max(seg[pos<<1].vals+seg[pos<<1|1].num7,seg[pos<<1].num4+seg[pos<<1|1].vals);
	seg[pos].valx=max(seg[pos<<1].valx+seg[pos<<1|1].num4,seg[pos<<1].num7+seg[pos<<1|1].valx);
	seg[pos].num4=seg[pos<<1].num4+seg[pos<<1|1].num4;
	seg[pos].num7=seg[pos<<1].num7+seg[pos<<1|1].num7;
	return;
}

建树就没什么可说的了,和正常的线段树建树一样,最长不上升子序列长度和最长不下降子序列的长度都赋为\(1\)

inline void build(re ll pos,re ll l,re ll r)
{
	seg[pos].l=l;
	seg[pos].r=r;
	seg[pos].mid=l+r>>1;
	if(l==r)
	{
		seg[pos].vals=seg[pos].valx=1;
		seg[pos].num4=(a[l]==4);
		seg[pos].num7=(a[l]==7);
	}
	else
	{
		build(pos<<1,l,seg[pos].mid);
		build(pos<<1|1,seg[pos].mid+1,r);
		pushup(pos);	
	}
	return;
}

修改操作就直接把最长不上升子序列的长度和最长不下降子序列的长度交换,\(4\)的数量和\(7\)的数量交换。

标记下传

inline void add(re ll pos)
{
	seg[pos].tag^=1;
	swap(seg[pos].num4,seg[pos].num7);
	swap(seg[pos].valx,seg[pos].vals);
	return;
}
inline void pushdown(re ll pos)
{
	if(!seg[pos].tag)
		return;
	add(pos<<1);
	add(pos<<1|1);
	seg[pos].tag^=1;
	return;
}

区间修改

inline void upgrade(re ll pos,re ll l,re ll r)
{
	if(seg[pos].l>=l&&seg[pos].r<=r)
		return add(pos);
	else if(seg[pos].l>r||seg[pos].r<l)
		return;
	pushdown(pos);
	upgrade(pos<<1,l,r);
	upgrade(pos<<1|1,l,r);
	pushup(pos);
	return;
}

每次询问的结果就是seg[1].vals

注意这个毒瘤的没有空格的输入就好了。

posted @ 2020-10-05 20:13  CelticOIer  阅读(73)  评论(1编辑  收藏  举报