E. Swap and Maximum Block 解题思路
核心问题分析
题意:给定一个长度为 的数组 。允许的操作是:任选一个 (),把数组按长度 分块之后,在某一块内部,把它的前半 个数和后半 个数整体交换位置。可以执行任意次这样的操作。问操作完之后,数组的最大子段和最多能是多少。
看到“长度是 ”“按 分块、交换前后两半”这种描述,脑子里应该立刻弹出一棵满二叉树:把数组看成线段树的叶子,每一个内部节点管辖一段 长度的区间,而“允许交换前后两半”,翻译过来就是——每个内部节点都可以自由决定:它的左右两个子树,谁排在前面、谁排在后面。这是一道包裹在“数组操作”外壳下的树形 DP 题。
1. 把问题搬到线段树上
既然每个节点都能独立决定自己两个子树的左右顺序,那这道题本质上就是:给一棵满二叉树的每个内部节点各发一个开关(不翻转 / 翻转),求把所有开关调到最优状态后,整个数组的最大子段和。
线段树维护最大子段和是经典操作,每个节点要维护四个量:区间和 、最大前缀和 、最大后缀和 、最大子段和 。正常的(不能交换的)合并公式是:
现在这道题给了我们一个额外的自由度:每个节点在合并左右儿子时,可以选择先放右儿子、再放左儿子(把 互换)。
2. 关键洞察:四个量各自独立地选最优方向
乍一看会担心:如果 想要“不交换”、 想要“交换”,那这个节点到底该处于哪种状态?——但仔细想想会发现根本不需要纠结。因为 (以及右边同理)都已经是子树内部自由选择所有内部开关后能达到的最优值,它们本身互不冲突(子树内部的开关是子树自己的事,跟父节点无关)。父节点只是站在这些"子树已经调到最优"的四元组上面,再做一次合并,而这次合并本身也有"顺序"这一个额外自由度,所以只需要对 各自分别在“不交换”和“交换”两种合并方式里取最大值即可:
// 不交换:sum_L + pre_R 或 sum_R + suf_L 这类;交换:把 L R 互换再算一次
sum = sumL + sumR; // 加法交换律,其实这个不受交换影响
pre = max({preL, sumL + preR, preR, sumR + preL});
suf = max({sufR, sumR + sufL, sufL, sumL + sufR});
best = max({bestL, bestR, sufL + preR, sufR + preL});显然跟顺序无关,直接跳过讨论; 是“不交换时的前缀”和“交换时的前缀”取更大值; 同理; 除了继承左右子树内部的最优子段和,还要考虑跨越左右分界的那一段——不交换时是 ,交换后是 ,两者取更大的即可。