题解:P15325 【MX-X24-T6】「RiOI-7」Stardust:RAY
Register_int · · 题解
咋没人写,流泪。
先拆贡献。设最小的包含
注意到设
- 选择两个数
q_x,q_y ,并将二者交换。 - 给定
k ,求出\sum_{i\le k}\min_{j\le i}q_j ,\sum_{i\le k}\max_{j\le i}q_j 与\sum_{i\le k}\left(\min_{j\le i}q_j\right)\times\left(\max_{j\le i}q_j\right) 。
使用单侧递归线段树即可做到
究其原因是修改和查询太不平衡了。明明修改是简单的单点修改,查询却是这么大一坨。
考虑换维。扫描序列维,维护时间维。具体地,枚举
- 对于
[L,R] 内的所有i ,l_i\to\min(l_i,x) ,r_i\to\max(r_i,x) 。 - 复制一个新版本。
- 查询
i 上l_i ,r_i ,l_ir_i 的历史和。
那么就变成了两个我们熟悉的问题:区间 chkmin/max 与单点历史和。我们知道区间 chkmin/max 可以用 Segment Tree Beats 处理成区间对最值加,并且时间复杂度仍然为
先来看简单的情况:区间加,单点历史和。对于一个在
对于第三项
在一次修改时,第三类贡献是已经确定的,可以直接下传。但另两类贡献都有一个乘积项不确定。如何解决?
首先,在 Segment Tree Beats 修改的过程中,所有非最值的位置都不会被更改,且下传时最值一变标记就失效了,因此不用考虑一二类标记相互合并的情况。另外,以一类标记为例,其在下传至子区间时,我们会少算
事实上你可以发现,一类标记就是
那么我们可以写出来 pushdown:
inline
void upd(int p, const node &tag) {
if (t[p].x == tag.x) t[p].t1 += tag.t1;
if (t[p].y == tag.y) t[p].t2 += tag.t2;
if (t[p].x == tag.x && t[p].y == tag.y) t[p].t3 += tag.t3;
else if (t[p].x == tag.x) t[p].t3 += tag.t1 * t[p].y;
else if (t[p].y == tag.y) t[p].t3 += tag.t2 * t[p].x;
}
其中
于是我们解决了标记下传的问题。查询时直接下传到叶子处,查询对应的
我已经观察到一些素质很低的小朋友,用 AI 把 std 改了改就交上去了。因此代码不予放出。