JOISC2022 Fish2
有若干条鱼排成一排,若鱼 \(A\) 和鱼 \(B\) 相邻且 \(A\) 的大小不小于 \(B\),则鱼 \(A\) 可以吃掉鱼 \(B\),此时其大小加上鱼 \(B\) 的大小。到最后只剩下一条鱼成为赢家。
现在有 \(n\) 条鱼排成一排,大小依次为 \(a_1,a_2,\cdots,a_n\)。要支持 \(q\) 次操作,操作有两种:
- 把 \(a_x\) 改为 \(y\);
- 假如把下标在区间 \([l,r]\) 内的鱼拿出来做实验,问有几条鱼可能成为赢家。
\(n,q\le 10^5,1\le a_i\le 10^9\),时限 4s
题解:
考虑一次实验 \([l,r]\),暂时使 \(a_{l-1}=a_{r+1}\) 为 \(+\infty\)。
你想让一条鱼赢肯定是一直让它吃相邻的。
那么很显然,若存在 \(l\le l'\le r'\le r,[l',r']\ne [l,r]\),使 \(\sum_{i=l'}^{r'}a_i<\min(a_{l'-1},a_{r'+1})\),则这个区间里的鱼就没救了,我们叫它死亡区间;而如果一条鱼不属于任何死亡区间,它就可以赢。
也容易发现,把 \([l,r]\) 内的鱼拿出来建笛卡尔树的话,每个死亡区间对应着笛卡尔树的一个子树,因此死亡区间的总个数会是 \(O(n)\) 的。
然而有修改操作,所以这种笛卡尔树的结构不好维护。那么?
还记得 IOI2021 地牢游戏吗?注意这个关键点:\(A\) 吃完 \(B\) 后加的大小等于 \(B\) 的大小。所以说看起来可能会有类似“只会往外扩 log 次”的结论出现。
一次修改(把 \(a_x\) 改为 \(y\))可能对某个区间 \([l',r']\) 造成什么影响?
-
\(a_{l'-1}\) 改变(当 \(l'-1=x\) 时):
先把原先 \(l'-1=x\) 的死亡区间去掉,改完以后再把新的加上。删除和加入的过程是差不多的,关键是找到加入/删除的死亡区间有哪些。
我们从区间 \([x+1,x+1]\) 不断往右边扩展,即:
- 设当前区间为 \([x+1,r']\),尝试此区间是否可以加入/删除;
- 找到第一个 \(p\) 使得 \(p-1>r',a_p>\sum_{i=x+1}^{r'+1}a_i\),这个 \(p\) 可以线段树上二分来找到;
- 使 \(p-1\) 成为新的 \(r'\),然后返回第一步。
这样子每次往右扩时区间和 \(\sum_{i=x+1}^{r'}a_i\) 都要翻倍,因此扩的次数 \(O(\log\max a_i)\)。
也容易证明加入/删除的死亡区间一定在扩展到的区间当中。
-
\(a_{r'+1}\) 改变(当 \(r'+1=x\) 时),处理方式同上。
-
\(\sum_{i=l'}^{r'}a_i\) 改变(当 \(x\in [l',r']\) 时):
同样先删再加,关键问题还是找到加入/删除的死亡区间有哪些。这时我们不能只往一边扩展了。
我们从区间 \([x,x]\) 不断往外扩展。设当前区间为 \([l',r']\),如果 \(a_{r+1}<a_{l-1}\),就往右扩展一次,否则往左扩展一次。
为什么这样是对的呢?假如 \(a_{r+1}<a_{l-1}\) 不往右扩展而往左扩展,那么新的区间和一定大于等于 \(a_{l-1}\),更大于等于 \(a_{r+1}\),所以这不会是一个死亡区间。
同理,扩的次数也是 \(O(\log \max a_i)\) 的。
查询时就是查一段区间内有多少位置没有被死亡区间覆盖。那就加入/删除死亡区间时,把这个区间做 +1/-1 操作,只要支持查询最小值个数,再拿个线段树即可(注意不是查询 \(0\) 的个数,这是考虑到可能有死亡区间 \([l',r']\) 包含 \([l,r]\),这样的死亡区间本不该算进去,却会使 \([l,r]\) 全都非 \(0\);而查询最小值个数是对的)。
这要求不能做多余的加入/删除操作(不然可能会多加或者多减),这是可以做到的。
时间复杂度 \(O(n\log n\log\max a_i)\)。