像潮落潮涌,送我奔向自由。|

寂静的海底

园龄:3年2个月粉丝:59关注:15

GYM103861F 解题报告

这大牛逼题在GYM里并没有任何题解是非常折磨的。

103861F

题意:区间查一个序列的长度不超过 c 的最大子段和,有负数,需要支持单点修改。

n2105,q5105

4s,512MB

点开AC人里面发现有一个 2log 的 std 和一个不明的应该是单 log 的猛猛 std(?)和几个分块老哥总共也没几个人写了。

提出一个单 log 做法。

在考虑这种神仙东西之前先考虑另一个同样值得思考的问题:如何求出长度至少 c 的最大子段和?

求最大子段和一直有三种方法,第一种是线段树维护区间前后缀最大子段和然后合并,第二种是广义矩阵乘法,第三种冷门的办法是使用一个类似 Serious Business 一样维护 A(x)+B(y)(xy) 的线段树分治结构,令 A(x)=S(x),B(x)=S(x) 维护前缀和,支持区间加就行了。

考虑前面两种方法都不好表示长度,使用第三种,其实就是维护S(x)S(y) (yxc) 的最大值,直接令 A(x)=S(xc),B(x)=S(x) 即可,形象地说就是将前缀和错位了 C 格后线段树维护分治结构,在这棵线段树上查询 [l+c,r] 即可。

考虑这个题,要是同样的将前缀和反着错位 c 位,护S(x)S(y) (0yxc) 的最大值,直接令 A(x)=S(x+c),B(x)=S(x) 我们无法限制这个 0 的左界让人非常头疼, 直接让前缀和做差可能会求出一个线段的和的相反数,即 yx。考虑如何限制这一点。

我们将数组割开成大小为 c 的段(或许 c1 会好写不少?),这样就只需要考虑整段直接在普通的线段树上查最大子段和维护到一棵别的线段树上,剩下就只用考虑经过两个段的答案了,给每两个段之间开一个线段树,维护段与段之间的最大子段和,这个时候就可用类似上面的线段树维护分治结构解决问题了。

维护错位的“前一块的后缀和 S(x) 和 后一块的前缀和 P(x)”,此处 p 和 s 使用的都是块内的编号。

就是维护 P(x)+S(y)(x<y) 就能求出答案了。


对于一次修改:

修改整棵最大子段和线段树的答案并更新维护答案的线段树

对前面块的后缀和进行前缀加并更新维护答案的线段树

对后面块的前缀和进行后缀加并更新维护答案的线段树


对于一次查询:

kl,kr 为左右端点所在的块, l,r 分别为左右端点的块内编号。

1\degree (RLc)

直接查询在最大子段和的线段树上的答案即可。

2\degree (RLc),kr=kl+1

两个端点分别在两个相邻的块。

l,r 分别为左右端点的块内编号。

一种较为复杂的情况,根据 l,r 将这两个块之间的段的分成三部分[1,l),[l,r],(r,c] , 方便称为 Pt1,Pt2,Pt3

发现答案有一下可能性:

i. Pt2内部的 P,S

ii. P(Pt1)+S(Pt2)

iii. P(Pt2)+S(Pt3)

iv. P(Pt1)+S(Pt3)

分别在线段树上查询即可。


3\degree (RLc),kr>kl+1

先在维护答案的线段树上查询中间部分 [kl+1,kr1] 的答案,让后考虑边部分的答案。

以左侧为例子。

会发现这是上面 2 这种情况的弱化版本。

分成 [1,l),[l,r] 两部分,称为 Pt1,Pt2

答案要么是 Pt2 内部的 P,S 要么是 P(Pt1)+S(Pt2)

右侧类似。

还要分别查询 [L,BlockRkl][BlockLkr,R] 内的最大子段和,这两者没有跨过任何块故没有被计算到。


时间复杂度 O(nlogn+t×qlogn) 其中 t=8

由于人傻常数较大,跑了两秒多,但是非常有卡常空间,所以本题数据还能拉大搞死分块人(?)。


长达 12k 的 恶臭代码。

传送门ღꦿ࿐


参考了 qwerty787788 在讨论区中的解法。

感谢可爱的 45dino 让我知道这道题/qq

posted @   寂静的海底  阅读(22)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起