解题报告:luogu P2572
脑子笨,切不了 Ynoi,只能做比较简单的 ds 题了/kk。
题目链接:P2572 [SCOI2010]序列操作
思路很简单,让我们来看看吧。
先不分 \(0/1\):
首先发现操作 \(4\) 是最难维护的,我们联想线段树求区间最大子段和的套路即可:记录一个 pre(前缀)
,suf(后缀)
来记录从区间第一个数开始的串的长度和从区间最后一个数开始的串的长度。
那么这个区间最长的区间(\(res\))为
有点多啊......
对于总数,用一个 \(sum\) 维护即可。
这么简单?
依分析可以得出update
函数:
void update(int k)
{
a[k].sum[0]=a[k<<1].sum[0]+a[k<<1|1].sum[0];
a[k].sum[1]=a[k<<1].sum[1]+a[k<<1|1].sum[1];
if(a[k<<1].pre[0]==a[k<<1].r-a[k<<1].l+1) a[k].pre[0]=a[k<<1].pre[0]+a[k<<1|1].pre[0];
else a[k].pre[0]=a[k<<1].pre[0];
if(a[k<<1].pre[1]==a[k<<1].r-a[k<<1].l+1) a[k].pre[1]=a[k<<1].pre[1]+a[k<<1|1].pre[1];
else a[k].pre[1]=a[k<<1].pre[1];
if(a[k<<1|1].suf[0]==a[k<<1|1].r-a[k<<1|1].l+1) a[k].suf[0]=a[k<<1|1].suf[0]+a[k<<1].suf[0];
else a[k].suf[0]=a[k<<1|1].suf[0];
if(a[k<<1|1].suf[1]==a[k<<1|1].r-a[k<<1|1].l+1) a[k].suf[1]=a[k<<1|1].suf[1]+a[k<<1].suf[1];
else a[k].suf[1]=a[k<<1|1].suf[1];
a[k].res[0]=max(a[k<<1].res[0],max(a[k<<1|1].res[0],max(a[k].suf[0],max(a[k].pre[0],a[k<<1].suf[0]+a[k<<1|1].pre[0]))));
a[k].res[1]=max(a[k<<1].res[1],max(a[k<<1|1].res[1],max(a[k].suf[1],max(a[k].pre[1],a[k<<1].suf[1]+a[k<<1|1].pre[1]))));
}
一定要用心,否则调到天荒地老。
修改很简单,但是暴力改会 \(T\) 掉,所以借助“懒标记”来优化。
所以难点出现了。懒标记的下传和叠加:
我们先看操作 \(0\) 的懒标记:
发现要把两个儿子所有 关于串 \(0\) 的信息都改到最大(a.r-a.l+1
)即可,对于 所有 关于串 \(1\) 的信息都改到最小(0
)即可。
最难的是叠加,容易漏掉:
有几种要考虑的情况:
\(1.\) 这个点先前有标记要全改成 \(1\):显然没用了,因为全改成 \(1\)之后还要改成 \(0\),那么把那一个清空,更新 \(0\) 的标记;
\(2.\) 这个点要全部取反:显然不管怎么操作最后都要沦为 \(0\),去掉从前的,弄成现在的。
操作 \(1\) 的懒标记只是把上面那个反过来。
操作 \(2\) 的懒标记:
首先把关于 \(0\) 的信息和关于 \(1\) 的信息全部互换。
看一些情况:
\(1.\)下面那个区间已打上这个标记,两个相反等于没有,抹掉标记;
\(2.\)下面没有此标记,但有全改成 \(0/1\) 的标记。显然改成全改为 \(1/0\) 的标记即可。
\(3.\)如果并没有任何标记,下传即可。
然后你会发现:一个点最多有一种标记!
所以其实一个 lazytag
即可。
懒标记下传代码:
void lazydown(int k)
{
if(a[k].l==a[k].r){a[k].lazy[0]=a[k].lazy[1]=a[k].lazy[2]=0;return;}
if(a[k].lazy[0]==1)
{
a[k<<1].res[0]=a[k<<1].suf[0]=a[k<<1].pre[0]=a[k<<1].sum[0]=a[k<<1].r-a[k<<1].l+1;
a[k<<1|1].res[0]=a[k<<1|1].suf[0]=a[k<<1|1].pre[0]=a[k<<1|1].sum[0]=a[k<<1|1].r-a[k<<1|1].l+1;
a[k<<1].res[1]=a[k<<1].suf[1]=a[k<<1].pre[1]=a[k<<1].sum[1]=0;
a[k<<1|1].res[1]=a[k<<1|1].suf[1]=a[k<<1|1].pre[1]=a[k<<1|1].sum[1]=0;
if(a[k<<1].lazy[1]) a[k<<1].lazy[1]=0;
a[k<<1].lazy[2]=0;
a[k<<1].lazy[0]=1;
if(a[k<<1|1].lazy[1]) a[k<<1|1].lazy[1]=0;
a[k<<1|1].lazy[2]=0;
a[k<<1|1].lazy[0]=1;
}
else if(a[k].lazy[1]==1)
{
a[k<<1].res[1]=a[k<<1].suf[1]=a[k<<1].pre[1]=a[k<<1].sum[1]=a[k<<1].r-a[k<<1].l+1;
a[k<<1|1].res[1]=a[k<<1|1].suf[1]=a[k<<1|1].pre[1]=a[k<<1|1].sum[1]=a[k<<1|1].r-a[k<<1|1].l+1;
a[k<<1].res[0]=a[k<<1].suf[0]=a[k<<1].pre[0]=a[k<<1].sum[0]=0;
a[k<<1|1].res[0]=a[k<<1|1].suf[0]=a[k<<1|1].pre[0]=a[k<<1|1].sum[0]=0;
if(a[k<<1].lazy[0]) a[k<<1].lazy[0]=0;
a[k<<1].lazy[2]=0;
a[k<<1].lazy[1]=1;
if(a[k<<1|1].lazy[0]) a[k<<1|1].lazy[0]=0;
a[k<<1|1].lazy[2]=0;
a[k<<1|1].lazy[1]=1;
}
if(a[k].lazy[2]==1)
{
swap(a[k<<1].suf[0],a[k<<1].suf[1]);
swap(a[k<<1].pre[0],a[k<<1].pre[1]);
swap(a[k<<1].sum[0],a[k<<1].sum[1]);
swap(a[k<<1].res[0],a[k<<1].res[1]);
swap(a[k<<1|1].suf[0],a[k<<1|1].suf[1]);
swap(a[k<<1|1].pre[0],a[k<<1|1].pre[1]);
swap(a[k<<1|1].sum[0],a[k<<1|1].sum[1]);
swap(a[k<<1|1].res[0],a[k<<1|1].res[1]);
if(!a[k<<1].lazy[2])
{
if(!(a[k<<1].lazy[1]||a[k<<1].lazy[0]))a[k<<1].lazy[2]=1;
else swap(a[k<<1].lazy[1],a[k<<1].lazy[0]);
}
else a[k<<1].lazy[2]=0;
if(!a[k<<1|1].lazy[2])
{
if(!(a[k<<1|1].lazy[1]||a[k<<1|1].lazy[0])) a[k<<1|1].lazy[2]=1;
else swap(a[k<<1|1].lazy[1],a[k<<1|1].lazy[0]);
}
else a[k<<1|1].lazy[2]=0;
}
a[k].lazy[0]=a[k].lazy[1]=a[k].lazy[2]=0;
return;
}
其他的东西十分 naive,不贴代码了 quq,(其实是防抄袭)
跑得好慢啊
最后跟一下风:
公元 2020 年 5 月 8 日,北京时间 21 点 34 分 44 秒,洛谷用户 hanzhongtlx 在经过 25 小时的奋战之后,成功 AC P2572 [SCOI2010]序列操作。
以此纪念。
(那个时间不准,做题时间大概只有不到 \(4h\)。