P7482 不条理狂诗曲

传送

首先我没读完题,看着那个 \(f(l,r)\) 就照着单点修区间查的样子去想了。

区间查的情况可以视为一个 \(dp\),从左到右求最大独立集,这个 \(dp\) 很好想也很好写,但是单次是 \(O(n)\) 的,太慢。

于是,单点修区间查,这啥?不说话,先套线段树。

发现这个 \(dp\) 过程可以分段,对于每一段记录选不选左端点右端点一共四种情况的最大值,合并是容易的。

然后就觉得这题做完了,回去一看:

\[\sum_{l=1}^n\sum_{r=l}^n f(l,r) \]

所以我在想什么来着?诶我有个好点子。

全局求和这种东西分治用的比较多,所以考虑分治,然后发现这个分治的方法和线段树的方法一个模子里刻出来的。

具体一点,我们在处理 \([L,R]\) 这个区间,它有两个儿子 \([L,mid]\)\([mid+1,R]\),我们只需要考虑 \(l\in[L,mid]\)\(r\in [mid+1,R]\) 里的 \(f(l,r)\) 就行。

这时候考虑儿子传什么信息上来。线段树里传的是两个儿子选不选左端点右端点的最大值,那么要求所有的区间就传每个前缀选不选左端点的最大值和每个后缀选不选右端点的最大值。但是主播主播,这样朴素合并太慢了,有没有什么好的方法?

有的兄弟,有的。

我们先设左儿子传的选 \(a_{mid}\) 的后缀叫做 \(suf1_l\sim suf1_{mid}\),不选 \(a_{mid}\) 的后缀叫做 \(suf2_l\sim suf2_{mid}\)。右儿子传的选 \(a_{mid+1}\) 的前缀叫做 \(pre1_{mid+1}\sim pre1_r\),不选 \(a_{mid+1}\) 的前缀叫做 \(pre2_{mid+1}\sim pre2_r\),然后就可以有一个表达式叫做 \(f(l,r)=\max\{suf1_l+pre2_r,suf2_l+pre1_r,suf2_l+pre2_r\}\)

这个东西 \(\max\) 里有三项还是太丑陋了,所以我们合并一下让里面变成 \(f(l,r)=\max(\max(suf1_l,suf2_l)+pre2_r,suf2_l+pre1_r)\),再来考虑取哪一项的问题。

如果选择后一项,那么就有 $\max(suf1_l,suf2_l)+pre2_r<suf2_l+pre1_r $ \(\Leftrightarrow \max(suf1_l,suf2_l)-suf2_l<pre1_r-pre2_r\) \(\Leftrightarrow \max(suf1_l-suf2_l,0)<pre1_r-pre2_r\)

诶只剩下一个 \(\max\) 了!于是我们再让左儿子传一个 \(\max(suf1_l-suf2_l,0)\)(当然现求也行)然后给它记上标记再从小到大排序,记后缀和。对于每个 \(r\),二分出 \(\max(suf1_l-suf2_l,0)<pre1_r-pre2_r\)\(l\) 个数,对于这部分 \(l\),都取 \(pre1_r-pre2_r\) 作为 \(f(l,r)\),这部分乘一下即可;剩下的取 \(\max(suf1_l-suf2_l,0)\) 作为 \(f(l,r)\),加上对应的后缀和就好了。

记得取模,该取模的时候一个运算符取一次,不该取模的时候别取。总复杂度两只 \(\log\)

posted @ 2025-04-24 10:40  Xuan_tmp  阅读(7)  评论(0)    收藏  举报