离独立想出正解只差一步了。
我的做法是使用网络流武器,抛弃了贪心的思考。虽然没有锻炼到贪心能力,但是加深了对网络流的理解吧。
考虑撤销可以用线段树分治,故只考虑加入的情况。我们发现这个模型很像费用流,于是考虑建模。
-
源点向所有员工连边,容量为 \(1\),费用为其能力值。
-
所有员工向其初始所在节点连边,容量为无穷,费用为 \(0\)。
-
每个点向其儿子(如果有)连边,容量为无穷,费用为 \(0\)。
-
每个点向汇点连边,容量为 \(1\),费用为 \(0\)。
然后跑最大费用流。
我们考察到加入一个员工时,只需要往这个员工的方向增广一条路径。此时这条路径要么是源点到汇点的一条路径,要么是从源点出发,又回到源点的一条回路。那么情况就清晰起来了:
对于第一类路径,就是找到一个能到的点(注意这个点指的是能通过走没流满的边能到达的点,下同),并且这个点向汇点的边没满流(这个点没被占用),就向其增广。
对于第二类路径(也就是不存在第一类路径),也是找到一个能到的点,并且往上走向这个位置所包含的某个员工,然后回到源点,结束,也就是顶替掉它。可以发现这个员工一定是以该点为起始点的现存员工中能力值最小的。
然后把树上的边增广(正向流量 \(+1\),反向流量 \(-1\)),再维护一下每个树上点是否被占用,并用 multiset
之类的东西维护现存员工。
找能到达的点的时候,就可以先一直跳祖先,直到它和父亲的边没有流量。然后在跳到的这个位置的所有子树内选择。树上的这些路径操作都可以树剖简单维护。选择的这个点就是子树查询到每个点能够产生的贡献的最小值,答案加上新员工能力值减去这个最小值即可。这个用一个线段树即可。
再套上线段树分治,时间复杂度 \(\mathcal O(n\log^3n)\),空间复杂度 \(\mathcal O(n\log n)\)。当然如果用全局平衡就可以把时间复杂度降成 \(\mathcal O(n\log^2n)\)。