pht春4
A
给出一种比较复杂的构造
每个点连向的那堆点每一次都新建一个点,只要两个操作同时操作一个格子,那就肯定有先后顺序,通过这样子就可以建出一个DAG,现在只需要构造一个非1-n的拓扑序即可,这可以把拓扑排序拿个大根堆实现。
盲猜可重排只需要交换相邻两个
如果他们交集后面被别人覆盖那样子就可以
B
只要有两个
首先是不是有4个就肯定不行?正常情况下4个的贡献可以使 (40)(13)(31)(04)(22)(11)(02)(20)
,其中只有一些...
有点乱。一个排列合法只需要满足:
- 两种括号分别守恒
- 没有
(] [)
的情况
否则一定合法。
如果 n 2 n^2 n2 可做,只需要枚举分别匹配程度以及最后一个的方向。
那样子可以把相邻两个字符之间的连接方式拆成4种点,其中上面那两种不合法,每个点的后继一定(2个)。然后每条边可以看做在某个维度的费用。然后从起点到终点,使得总费用为0。
现在就是把dp改成了自动机形式,没有什么大作用。
每种类型的边需要走的次数是固定的。
先看能不能找出一种方式,直接用栈就近原则匹配。
复杂做法:
每次插入一个东西到栈之前先求一下当前栈里的哈希值
如果有相同哈希值,说明这一段都可以。
也就是说如果两次出现的哈希值一样然后好像就行什么的?
简单做法:
如果出现两次以上相邻两个是一样的
也就是三个 a i = a i + 1 a_i=a_{i+1} ai=ai+1
或者有三个相同。
C
两种操作,加入一条横线或一个绝对值函数。加入横线操作在外面记录一下。然后绝对值的维护李超来一下就行?
其实不用,典型的油站问题,只需要取最中间的那个点即可。
此时怎么求值呢?相当于要维护前后后缀和。可以动态开点线段树,可以离线然后树状数组,当然直接拿个指针似乎也可行?
是的,直接拿一个指针再加上set就很好维护了。
对顶堆实现会更好
D
绝对在哪里做过。
突然发现直接上 n 2 n^2 n2 好像可以。每个数最后最多只会 + − n +-n +−n,但那样子转移还需要考虑一下。
令 a i ′ = a i − i a'_i=a_i-i ai′=ai−i,变成非严格递增
然后会有一个 O ( n V ) O(nV) O(nV) 的dp。 d p ( i , p ) dp(i,p) dp(i,p) 表示第 i i i 个数调整为 p p p 的最小代价。 d p ( i , p ) = min q ≤ p d p ( i , q ) + ∣ a i ′ − p ∣ dp(i,p)=\min_{q\le p} dp(i,q)+|a'_i-p| dp(i,p)=minq≤pdp(i,q)+∣ai′−p∣
显然后面的 V V V 不是所有有用,显然只有 a i ′ a'_i ai′ 是有用的。
因此状态变成 O ( n 2 ) O(n^2) O(n2)。 min \min min 拿个前缀优化搞一下就行了。
加强一下 n ≤ 1 0 5 n\le 10^5 n≤105
d p ( i , x ) dp(i,x) dp(i,x) 映射到 f i ( x ) f_i(x) fi(x) 变化过程应该是凸的(类似一个开口向上的二次函数)
f i ( x ) = min x ′ < x f i − 1 ( x ′ ) + ∣ a i − x ∣ f_i(x)=\min_{x'<x}f_{i-1}(x')+|a_i-x| fi(x)=minx′<xfi−1(x′)+∣ai−x∣
前面那个东西里面本身是凸的。相当于这个二次函数到达低谷后就一马平川。
分讨一下 a i a_i ai 的情况,假设在 L L L 位置开始一马平川。
- a i > L a_i>L ai>L,相当于加一个很丑的绝对值函数。使得右边是上升,左边全部偏移一个斜率,此时最优值会在绝对值端点处取得。此时仍然是凸的。
- 落在左边。绝对值函数左边又会上升一个阶段,右边下降,仍然是凸的。此时关心最值,始终维护低谷 [ L , R ] [L,R] [L,R],最低点的取值。
做最前面的 min \min min 就是把 R R R 设为正无穷。
L L L 左侧维护了斜率的递增的拐点,用最大堆来维护。
思考任意时刻拐点的变换,每一次变化斜率都会+1。每一次相当于加入一个斜率,弹出一个东西,或者在右边不知道干什么。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
给出刚才C的另一个做法。
维护那个斜率也可以类似这么搞。
E
上周原题。
上周做法:
首先转化和我的一样。
对于上面那个 f f f,显然 j j j 只会转移到 j + 1 , j , j − 1 j+1,j,j-1 j+1,j,j−1 ,而且如果比他小只会+1,比他大只会-1。
考虑最终答案肯定是 min f [ n ] [ 0 ... n ] \min f[n][0\dots n] minf[n][0...n]。但最小值一定是 f [ n ] [ 0 ] f[n][0] f[n][0],因为最后一个显然取最小是没问题的。
所以前 i i i 个的最优在 f [ i ] [ 0 ] f[i][0] f[i][0] 处取得,有 d p i = f i , 0 dp_i=f_{i,0} dpi=fi,0,但这样没法做转移。
比如 a i < a i − 1 a_i<a_{i-1} ai<ai−1,从 j − 1 j-1 j−1 的转移本质是当前额外多加个 k k k,相当于把代价变成了 k − d i k-d_i k−di。我们把 k − d i k-d_i k−di 仍到一个堆里,相当于可以在某个地方额外往上加一层。
好,现在考虑会 a i > a i − 1 a_i>a_{i-1} ai>ai−1,现在本身要花费 d i d_i di 的代价,但如果你用之前的 k − d i k-d_i k−di,那么就不需要执行这个操作了。
其实就是要么按原计划,要么在之前下沉一轮。
那现在需不需要把 d i d_i di 放进去呢?
嗯,是需要的,因为未来可能是有更差的。
然后现在又来神秘斜率维护做法。
F
假设 T N ≤ 1 0 5 T_N\le 10^5 TN≤105
首先假设他不动,然后他受哪些水枪的影响是易求。
左边已经受影响、左边未受影响,右边的也可以,总共维护4个set/priority_queue。假设后面全部不变,只改变当前这一步,往左和往右的贡献可以算的。
但我感觉这个做法中假设后面不动和往左右的贡献的计算本身就是矛盾的?
可不可以类比成加入一次函数呢?
类似T2那样有一个 O ( n B ) O(nB) O(nB) 的方程。
d p ( i , x ) dp(i,x) dp(i,x) 表示在第 i i i 个事件时站在 x x x 处的最小伤害。
设时间差为 t i t_i ti,则有:
d p ( i , x ) = min y ∈ [ x − t i , x + t i ] d p ( i − 1 , y ) + max ( a i − x , 0 ) dp(i,x)=\min_{y\in[x-t_i,x+t_i]}dp(i-1,y)+\max(a_i-x,0) dp(i,x)=y∈[x−ti,x+ti]mindp(i−1,y)+max(ai−x,0)
考虑变成图像。首先 f i ( x ) f_i(x) fi(x) 肯定是凸的。
对于第一个操作,就是掰开来,然后往右移动 t t t 的长度,左边的也是往左。就是相当于都会往左边或右边"抄过来"!
由于问的是最小值,所以只需要维护最低谷的段 [ L , R ] [L,R] [L,R]。然后现在有 L ′ = L − t , R ′ = R + t L'=L-t,R'=R+t L′=L−t,R′=R+t。此时显然仍然是凸的。
考虑第二个操作张什么样子。
在 a i a_i ai 处,右边全为0,左边是斜率为1的d东西。
现在开始分讨:
- L ≤ a i ≤ R i L\le a_i \le R_i L≤ai≤Ri,相当于左边斜率都+上一个-1。
- a i < L a_i<L ai<L,前面增加-1段。
- a i > R a_i>R ai>R,前面加一个负1段。右边是不变的,左边是没那么陡了。
明确,要知道 f ( 0 ) f(0) f(0)?我们要知道最低谷的段长和左右的斜率。
可以把里面斜率发生的时间点的转折的坐标点扔到堆中维护。
需要维护两个堆, L L L 左侧最大堆, R R R 右侧最小堆。
对于第3种情况,扔一个 a i a_i ai 进去,然后拿一个出来,再扔一个进去。动态维护 L , R L,R L,R,以及其对应的值。因为这样子你就可以知道每个点的值。
最后求 f ( − ∑ , t i ) f(-\sum,t_i) f(−∑,ti) 到 f ( ∑ , t i ) f(\sum,t_i) f(∑,ti)。相当于一个区间的的最小值。那么只需要判断一下和低谷区间的关系即可。
本质:维护点值以此斜率。
码量很小,但想清楚还是存在一点难度的
G
距离为切比雪夫距离
只要某个坐标过了某个点,继续往这个方向走是一定不利的。
套路转化:
max ( ∣ x 1 − x 2 ∣ , ∣ y 1 − y 2 ∣ ) \max(|x_1-x_2|,|y_1-y_2|) max(∣x1−x2∣,∣y1−y2∣),切比雪夫转曼哈顿。 x ′ = x + y , y ′ = x − y x'=x+y,y'=x-y x′=x+y,y′=x−y。变成了: ∣ x ′ − x ∣ + ∣ y ′ − y ∣ 2 \dfrac{|x'-x|+|y'-y|}2 2∣x′−x∣+∣y′−y∣,除2显然可以搞走,现在就是要去做上面的东西。
现在应该是可以走1或者走-1。但是始终是往右走。相当于每次往右,可以向上或向下。
显然种土豆肯定是和土豆横坐标相等的时候再来种。
因为每走一步要么更近,要么不变。
剩下的就和上面的一样了。
把 x x x 看成 t t t,但不能走0。但因为奇偶性的原因走0显然是不优的,所以可以当做有0。
我们之前是: d p ( i , x ) = min y ∈ [ x − t i , x + t i ] d p ( i − 1 , y ) + max ( a j − x , 0 ) dp(i,x)=\min_{y\in[x-t_i,x+t_i]}dp(i-1,y)+\max(a_j-x,0) dp(i,x)=miny∈[x−ti,x+ti]dp(i−1,y)+max(aj−x,0),现在变成了 d p ( i , x ) = min y ∈ [ x − t i , x + t i ] d p ( i − 1 , y ) + ∣ a i − x , 0 ∣ dp(i,x)=\min_{y\in[x-t_i,x+t_i]}dp(i-1,y)+|a_i-x,0| dp(i,x)=miny∈[x−ti,x+ti]dp(i−1,y)+∣ai−x,0∣
就是把后面变成了绝对值。
挺好打,在上题基础上简单修改即可
H
看了一大轮中英文题面都没看懂?
两个人参加XCPC
已知 A A A 会在时间轴 a i a_i ai 会有成功提交
知道 B B B 解决每道问题的时间 b i b_i bi。他可以不立刻提交,他只希望正常比赛有最多的时间满足上一次提交的是他。
做题顺序显然按 b i b_i bi 升序。
把事件提取出来:
- a i a_i ai 出现
- 某个 b b b 的前缀和。
只能在这些时间点交题。
我们在数轴上圈出来。
刻画出一些关键 b b b。
从后往前扫,遇到一个 a a a,就把这个 a a a 和下一个的差值塞进去存起来,遇到 b b b 就取出最大的一段。隐藏条件:只在 a a a 处搞。
举例:B A A
。
反例说明:
- 要么为 A A A
- 要么是A之后第一个B
考虑这两种情况:
- ∣ l s t A − c u r B ∣ ≤ T o p |lstA-curB|\le Top ∣lstA−curB∣≤Top,显然直接贪心
- ∣ l s t A − c u r B ∣ > T o p |lstA-curB|> Top ∣lstA−curB∣>Top,当前最优取这一段 [ l s t A , c u r B ] [lstA,curB] [lstA,curB]。未来呢?反悔呢?
离 l s t A lstA lstA 很近的地方有个 B B B,他来做这一段,再腾出手来做下一段。
反悔是:删这一段 + 加全段 + 加Top段。感觉后面只要搞到Top这一段那么就一定要反悔,因此Top段可以直接pop掉了
然后这个东西拿个堆维护即可。
这就是一个反悔贪心。
I
先假设根是定的。
然后每个点到所有叶子经过的黑色节点数量相同。
首先对于每个儿子内部肯定合法。
此时已经有一个 O ( n 2 ) O(n^2) O(n2) 的做法,直接 f [ i ] [ j ] f[i][j] f[i][j] 表示子树 i i i 内到每个叶子的黑色节点都是 j j j 至少要改多少次。
按照经典套路,把点的子树改成边的子树那样子就可以直接对每个根求了。那样子这题的总复杂度是 O ( n 2 ) O(n^2) O(n2)。
现在考虑优化。
假如我知道一棵树的状态,我能不能瞬间求出每个叶子+1的代价。
显然在越浅层来做肯定代价越小,然后还没
一个暴力dp, f ( u , k ) f(u,k) f(u,k) 表示 u u u 子树内每条链恰好有 k k k 个。
转移: f ( u , k ) = min ( ∑ v ∈ u f ( v , k − 1 ) + 1 , ∑ v ∈ u f ( v , k ) ) f(u,k)=\min(\sum_{v\in u}f(v,k-1)+1,\sum_{v\in u}f(v,k)) f(u,k)=min(∑v∈uf(v,k−1)+1,∑v∈uf(v,k))(假设当前为红色点)
假如没有 ∑ \sum ∑,那么就和之前Slope Trick一样了。只是变成了+1而已。
相当于最低谷往右延伸一格。反过来就是往左延伸一格。这就是链的情况。
合并!两个凸的合并还是凸的,但是没那么好维护。但暴力做即可,总复杂度是 O ( n ) O(n) O(n) 的,复杂度和长剖一样。
因为瓶颈在于最小儿子,因为下面是没用的,可以直接cut掉。因此每次至少cut掉一棵子树。
每次都会砍掉。每个做了一次合并至少砍掉一半的东西。
关于合并:
每一个 f f f 函数,左右两边都是上升的。先求 f f f 的前 L L L 个元素。然后暴力合并即可。一个继承,其他往上面加。
属实好题,确实难打
对于这个下凸函数,常见做法就是维护差分
凸函数相加很好做,直接一开始按照较短长度合并即可
但是还有一个min+的东西很难搞
考虑上面这个过程,我们知道的是什么,一个差分数组,和 f ( u , 0 ) f(u,0) f(u,0) 的值,这个值是我们可以直接计算出来的。
然后分讨一下,发现无论当前点的颜色如何,只要我们修改了 f ( u , 0 ) f(u,0) f(u,0) 那么前面斜率为负的那一段的斜率是不用边的,这会在最底下那里加一个点。
如果直接拿set维护是带 log \log log 的,但是每次只会加±1,所以拿两个vector维护,再加个变量维护0的那一段即可。
不知道怎么形容,有点抽象,慢慢分讨来打就好了。
J
一个树,然后分割,每个块大小给定,其方案数。
k k k 很小,直接类似树背包。
d p ( u , c ) dp(u,c) dp(u,c) 表示和 u u u 相连的连通块现在有 c c c 个, c ∈ [ 1 , k + 1 ] c\in[1,k+1] c∈[1,k+1]。
因为树形背包,所以是 O ( n k ) O(nk) O(nk) 的。
k k k 较大的时候。
定义 d p ( u , c ) dp(u,c) dp(u,c) 表示 u u u 的子树下切走了 c c c 个 k k k 的连通块,那么用 ( s i z e u − c × k ) m o d ( k + 1 ) (size_u-c\times k)\bmod (k+1) (sizeu−c×k)mod(k+1) 就得到 u u u 所在的联通块大小。那么显然有 c ≤ n k c\le \dfrac n k c≤kn。
因此总复杂度为 O ( n n ) O(n\sqrt n) O(nn )。
细节好多,写吐了。不过值得欣慰的是两种情况可以对拍。