通道
自己对于二分图构造一个类 prufer 序列。
一个映射方式是合法的,只需要:
- 一棵生成树能构造出一个 prufer 序列。
- 能从一个 prufer 序列逆推回整棵树的形态,即过程中不会出现 “找不到” 或者 “找到多个合法解” 的情况。
敢造就敢有!
排列
转化后题意:
给定三个长度同为 n n n 的序列 V , A , B V,A,B V,A,B,保证 V i > 0 V_i>0 Vi>0。
求选出一个区间 [ l , r ] [l,r] [l,r],最大化 ( ∑ i = l r V i ) + min i = l r ( A i ) + min i = l r ( B i ) \left(\sum_{i=l}^rV_i\right)+\min_{i=l}^r(A_i)+\min_{i=l}^r(B_i) (∑i=lrVi)+mini=lr(Ai)+mini=lr(Bi)。
n ≤ 1 0 6 n\leq 10^6 n≤106。
题解:
用数据结构容易做到 O ( n log n ) O(n\log n) O(nlogn),略去。此处讲线性做法:
看到区间 min,考虑建笛卡尔树,比如我们先对 A A A 序列建笛卡尔树。
笛卡尔树上一个点代表一段区间 [ L , R ] [L,R] [L,R],且该区间内的任意子区间 [ l , r ] [l,r] [l,r] 的 A min A_{\min} Amin 都相同,此时我们只需要考虑 [ L , R ] [L,R] [L,R] 的所有子区间 [ l , r ] [l,r] [l,r] 中 ( ∑ i = l r V i ) + min i = l r B i \left(\sum_{i=l}^rV_i\right)+\min_{i=l}^r B_i (∑i=lrVi)+mini=lrBi 的最大值即可。
一个很重要的观察是:若 L < l ∧ r < R L<l\land r<R L<l∧r<R,则 [ l , r ] [l,r] [l,r] 必为 B B B 序列笛卡尔树的一个区间。
因为若不是,那么我们可以往两边扩使得 B min B_{\min} Bmin 不变且 ( ∑ i = l r V i ) \left(\sum_{i=l}^rV_i\right) (∑i=lrVi) 变大,直到 [ l , r ] [l,r] [l,r] 为 B B B 序列笛卡尔树上的一个区间或者 l = L l=L l=L 或 r = R r=R r=R 为止。
所以我们先用 B B B 序列笛卡尔树上的所有区间更新一遍答案。这样 A A A 树上我们只需要考虑 l = L l=L l=L 或 r = R r=R r=R 的情况即可。
两种情况是类似的,不妨考虑 l = L l=L l=L,即区间为前缀的情况。
暴力的做法是用单调栈维护出 [ L , R ] [L,R] [L,R] 在 B B B 上前缀最小值的位置,假设它们分别为 p 1 = L , ⋯ , p k p_1=L,\cdots,p_k p1=L,⋯,pk,那么显然 r r r 只可能在 p 2 − 1 , ⋯ , p k − 1 p_2-1,\cdots,p_k-1 p2−1,⋯,pk−1 上取到(为了方便 l = L , r = R l=L,r=R l=L,r=R 的情况我们单独考虑)。
对于非暴力的方法,我们考虑合并两棵子树的单调栈。设左右儿子的 B B B 前缀最小值的单调栈分别为 B L , B R BL,BR BL,BR。
显然合并这两个栈是均摊 O ( 1 ) O(1) O(1) 的。但关键是怎么更新答案。
实际上我们只需要对于单调栈上的每一个点 p i p_i pi 求出 s p i + 1 − 1 + B p i s_{p_{i+1}-1}+B_{p_{i}} spi+1−1+Bpi 的最大值即可。
左右儿子合并时可能会把 B R BR BR 的一段头给弹掉,为了快速得到剩下一段后缀的 s p i − 1 + B p i − 1 s_{p_{i}-1}+B_{p_{i-1}} spi−1+Bpi−1 最大值,我们还需要另外一个单调栈来维护 s p i − 1 + B p i − 1 s_{p_{i}-1}+B_{p_{i-1}} spi−1+Bpi−1 的后缀最大值。
记左右儿子维护这玩意后缀最大值对应的单调栈分别为 M L , M R ML,MR ML,MR,那么我们合并两个儿子的整个算法流程如下:
- 合并 B L , B R BL,BR BL,BR,过程中会弹掉 B R BR BR 左边的一段,此时对应弹掉 M R MR MR 中属于这一段的部分,并做更新。
- 合并 M L , M R ML,MR ML,MR,过程中会弹掉 M L ML ML 右边的一段。
然后你发现我们有可能会对一个单调栈左右都弹,此时需要用一个链表来维护。
总时间复杂度 O ( n ) O(n) O(n)。