本人第一次出五校,出的就是这题。
奥法之劫 (offa)
题目信息
时间限制:1800 ms 1800 \texttt{ ms} 1 8 0 0 ms 。
空间限制:512 MiB 512 \texttt{ MiB} 5 1 2 MiB 。
本题使用文件输入输出。
输入文件名:offa.in
。
输出文件名:offa.out
。
题目背景
你很清楚地知道一颗小小的鹅卵石也会引起山崩。同样地,一次单纯的背叛居然引发 了魔法瘟疫,而其后果还在托瑞尔上狂舞肆虐着,且远不止于此。
– 阴影谷之伊尔明斯特,于 1479 DR,永恒者之年
术士: 终于到了么,因为奥法之疫而形成的幽暗深渊。
战士: 还没能看见深渊的时候,我们就碰到了不少疫变生物,感觉这一路上凶多 吉少啊。
游侠: 感想就之后再说吧,现在已经黄昏了,我们得赶紧找扎帐篷的地方了。
术士: 确实。嗯……七点钟方向的峡谷中有不少的废弃要塞,我们可以过去看看。
题目描述
游侠: 大致勘察了一下。这个峡谷是 U 形的,只有向西一个口子。而其中有 n n n 座废弃要塞,从西向东第 i i i 座要塞的编号为 i i i ,高度是 a i a_i a i 。
术士: 啧。这就有点尴尬了,我的想法是,给出一个长度为 m m m 的高度序列 { b i } \{b_i\} { b i } , 满足 ∀ 1 ≤ i < m , b i < b i + 1 \forall 1\le i<m,b_i<b_{i+1} ∀ 1 ≤ i < m , b i < b i + 1 ,然后选择这样的 m m m 座要塞满足:从西向东的编号分别为 q 1 , q 2 , ⋯ , q m q_1,q_2,\cdots,q_m q 1 , q 2 , ⋯ , q m ,对于所有的 1 ≤ i ≤ m 1\le i\le m 1 ≤ i ≤ m , a q i = b i a_{q_i}=b_i a q i = b i ,并且没有要塞向西的视线会被挡住, 即对于选出的每一座要塞,向西方向的其他要塞的高度都会严格小于这座要塞的高度。这样子高度非常合适。如果疫变生物来袭,可以有更佳的作战环境。但因为从远处看看不真切,忽略了除这 m m m 座要塞之外的会挡住视线的情况。
战士: 或许我们可以把其他的 n − m n-m n − m 座要塞拆掉,这样既可以搜刮这些要塞中的资源,又可以解决视线问题。这件事有术士你的身体强化法术应该并不是难事。
游侠: 我觉得这不太现实,就算有身体强化,凭我们仨,也要拆到深夜,这样耽误了休息时间,还有可能在拆除的时候被疫变生物攻击。
术士: 我倒觉得这不失为一个办法,但是我们可以更聪明一些。我们只需要拆掉一部分要塞,使得最后从其中可以选出 m m m 座要塞,从西向东的第 i i i 座要塞的高度要和 b i b_i b i 相同,并且选出的要塞都不会被挡住向西的视线。这样便可以解决问题了,也不需要花费太多时间。
战士: 我觉得上面的条件可以再加上一个条件:除了选出的 m m m 座要塞,其他未被拆除的要塞向西的视线都要被挡住,不能给敌人留下任何有视线开阔的要塞。
游侠: 我也说一下我的想法吧。根据我勘察的结果,拆除第 i i i 座要塞的代价是 p i p_i p i , 我们要尽量让拆除代价总和最小。
术士: 那我来做一个最后总结吧,现在我们要解决的问题描述如下:
有 n n n 座废弃要塞,从西向东第 i i i 座要塞的高度为 a i a_i a i ,拆除这座要塞的代价为 p i p_i p i 。 现在我们给定一个长度为 m m m 的高度序列 { b i } \{b_i\} { b i } ,满足
∀ 1 ≤ i < m , b i < b i + 1 \forall 1\le i<m,b_i<b_{i+1} ∀ 1 ≤ i < m , b i < b i + 1 。然后我们想要拆掉一部分要塞,使得最后从其中可以选出 m m m 座要塞,从西向东的编号分别是 q 1 , q 2 , ⋯ , q m q_1,q_2,\cdots,q_m q 1 , q 2 , ⋯ , q m ,满足:
对于任意的 1 ≤ i ≤ m 1\le i\le m 1 ≤ i ≤ m ,a q i = b i a_{q_i}=b_i a q i = b i ;
选出的要塞向西的视线都不会被挡住;
除了选出的 m m m 座要塞,其他未被拆除的要塞向西的视线都会被挡住。 最后要最小化拆除代价总和。
战士: 嗯。就是这样了。
游侠: 那么我们马上开工吧。
输入格式
从文件 offa.in
中读入数据。
输入的第一行包含两个正整数 n , m n,m n , m ,表示荒野中废弃要塞的数量,以及术士想要保留的废弃要塞的数量。
接下来一行,包含 n n n 个整数,第 i i i 个整数为 a i a_i a i ,表示从西向东第 i i i 座废弃要塞的高度。
再接下来一行,包含 n n n 个整数,第 i i i 个整数为 p i p_i p i ,表示从西向东第 i i i 座拆除废弃要塞的代价。
再接下来一行,包含 m m m 个整数,第 i i i 个整数为 b i b_i b i ,表示术士想要保留的从西向东第 i i i 座废弃要塞的高度。
输出格式
输出到文件 offa.out
中。
若可以找到一种拆除方案满足术士的计划,则输出一行最小的拆除代价总和; 否则输出 Impossible
。
样例
样例输入 1
1 2 3 4 5 11 1 3 1 2 6 8 7 7 4 11 10 0 9 11 -7 6 -5 0 3 -2 10 1 3 1 3 6
样例输出 1
样例解释 1
我们拆除第 4 , 6 , 7 , 8 , 9 , 10 , 11 4,6,7,8,9,10,11 4 , 6 , 7 , 8 , 9 , 1 0 , 1 1 座要塞,选取第 1 , 2 , 5 1,2,5 1 , 2 , 5 座要塞,这样拆除总代价为 − 7 − 5 + 0 + 3 − 2 + 10 + 1 = 0 −7 − 5 + 0 + 3 − 2 + 10 + 1 = 0 − 7 − 5 + 0 + 3 − 2 + 1 0 + 1 = 0 ,可以证明这是最小的拆除代价总和。
样例输入 2
1 2 3 4 5 6 1 6 2 2 3 5 -1 0 9 8 7 2 2 1 4
样例输出 2
样例解释 2
在原来的要塞中,不存在高度为 4 4 4 的要塞,所以不管怎么拆要塞都不能满足术士的要求。
样例 3
见选手目录下的 offa/offa3.in
与 offa/offa3.ans
。
本样例满足 Subtask #2 的限制。
样例 4
见选手目录下的 offa/offa4.in
与 offa/offa4.ans
。
本样例满足 Subtask #3 的限制。
样例 5
见选手目录下的 offa/offa5.in
与 offa/offa5.ans
。
本样例满足 Subtask #4 的限制。
样例 6
见选手目录下的 offa/offa6.in
与 offa/offa6.ans
。
数据范围与提示
对于所有的数据,1 ≤ m ≤ n ≤ 5 × 1 0 6 1\le m\le n\le 5\times 10^6 1 ≤ m ≤ n ≤ 5 × 1 0 6 ,1 ≤ a i , b i ≤ n 1\le a_i,b_i\le n 1 ≤ a i , b i ≤ n ,∣ p i ∣ ≤ 1 0 9 |p_i|\le 10^9 ∣ p i ∣ ≤ 1 0 9 ,且保证 ∀ 1 ≤ i < m , b i < b i + 1 \forall 1\le i<m,b_i<b_{i+1} ∀ 1 ≤ i < m , b i < b i + 1 。
Subtask #1 (13 points) :n ≤ 20 n\le 20 n ≤ 2 0 。
Subtask #2 (27 points) :n ≤ 1000 n\le 1000 n ≤ 1 0 0 0 。
Subtask #3 (10 points) :m = 1 m=1 m = 1 。
Subtask #4 (10 points) :{ a i } \{a_i\} { a i } 为 1 ∼ n 1\sim n 1 ∼ n 的排列。
Subtask #5 (20 points) :n ≤ 500000 n\le 500000 n ≤ 5 0 0 0 0 0 。
Subtask #6 (20 points) :无特殊性质。
提示:本题输入输出规模较大,请注意选择较为高效的输入方式(下发文件中的 offa/fast_input.cpp
可以拷贝使用)。
题解
原题链接
CF1334F Strange Function
数据范围有加强,加强思路来自题解下面的讨论区。
简要题意
定义两个序列之间的函数 f f f :给出一个长度为 l l l 的序列 { x i } \{x_i\} { x i } ,f ( { x i } ) f(\{x_i\}) f ( { x i } ) 为所有满足 1 ≤ i ≤ l , x i > x i − 1 , x i > x i − 2 , ⋯ , x i > x 1 1\le i\le l,x_i>x_{i-1},x_i>x_{i-2},\cdots,x_i>x_1 1 ≤ i ≤ l , x i > x i − 1 , x i > x i − 2 , ⋯ , x i > x 1 的 x i x_i x i 按照原序列次序组成的新序列。
给定一个长度为 n n n 的序列 { a i } \{a_i\} { a i } 和一个长度为 m m m 的序列 { b i } \{b_i\} { b i } ,在序列 { a i } \{a_i\} { a i } 中删除第 i i i 个数的代价为 p i p_i p i ,求最小的删除代价,使得 f ( { a i } ) = { b i } f(\{a_i\})=\{b_i\} f ( { a i } ) = { b i } ,或者给出无解。
算法一
若 { b i } \{b_i\} { b i } 不是 { a i } \{a_i\} { a i } 的子序列则无解。否则枚举每个数删或者不删,最后判断取最小代价。
时间复杂度为 O ( 2 n m ) O(2^nm) O ( 2 n m ) ,空间复杂度为 O ( n ) O(n) O ( n ) ,期望得分 13 13 1 3 分。
算法二
考虑 m = 1 m=1 m = 1 的情况,现在我们已经知道了要留下哪一个值。我们求出所有 a x = b 1 a_x=b_1 a x = b 1 的 x x x ,我们考虑如果 f ( { a i ′ } ) f\left(\{a'_i\}\right) f ( { a i ′ } ) 留下的位置只有 x x x 的话序列 { a i ′ } \{a'_i\} { a i ′ } 要满足什么条件。很显然的,要满足 a 1 ′ = b 1 a'_1=b_1 a 1 ′ = b 1 ,且 a 1 ′ a'_1 a 1 ′ 要为全局最大值。
那么我们枚举 x x x ,它的答案就是:
∑ i = 1 x − 1 p i + ∑ i = x + 1 n [ a i > b 1 ∨ p i < 0 ] p i \sum\limits_{i=1}^{x-1} {p_i}+\sum\limits_{i=x+1}^n{[a_i>b_1\vee p_i<0]p_i}
i = 1 ∑ x − 1 p i + i = x + 1 ∑ n [ a i > b 1 ∨ p i < 0 ] p i
两个和式可以分别用前、后缀和来维护,于是我们可以做到 O ( 1 ) O(1) O ( 1 ) 计算当前位置的答案
时空复杂度均为 O ( n ) O(n) O ( n ) ,综合前面算法,期望总得分 23 23 2 3 分。
算法三
考虑 { a i } \{a_i \} { a i } 是一个排列的情况,现在我们已经知道了要留下那些位置。设 a x = b i , a y = b i + 1 a_x=b_i,a_y=b_{i+1} a x = b i , a y = b i + 1 ,我们考虑 𝑥 与 𝑦 之间要删那些数。很显然,与算法二一样,a j > b i a_j>b_i a j > b i 或 者 p j < 0 p_j<0 p j < 0 的位置要删。那么我们直接模拟一遍就好了,还要注意 b 1 b_1 b 1 之前和 b m b_m b m 之后 的该删的也要删掉。
时空复杂度均为 O ( n ) O(n) O ( n ) ,综合前面算法,期望总得分 33 33 3 3 分。
算法四
考虑一个 D P DP D P ,设 f i , j f_{i,j} f i , j 表示我们考虑到了 { a i } \{a_i\} { a i } 的第 i i i 位,{ b j } \{b_j\} { b j } 第 j j j 位的最小代价总和。
转移方程分 a I < b j , a i > b j , a i = b j a_I<b_j,a_i>b_j,a_i=b_j a I < b j , a i > b j , a i = b j 三种情况:
若 a i < b j a_i<b_j a i < b j ,第 i i i 项元素可以删除也可以不删除,因为该元素不会加入新数组,对结果不产生影响,可以得到
f i , j = f i − 1 , j + min ( 0 , p i ) f_{i,j}=f_{i-1,j}+\min(0,p_i)
f i , j = f i − 1 , j + min ( 0 , p i )
若 a i > b j a_i>b_j a i > b j ,则 a i a_i a i 必须删除,因此
f i , j = f i − 1 , j + p i f_{i,j}=f_{i-1,j}+p_i
f i , j = f i − 1 , j + p i
若 a i = b j a_i=b_j a i = b j ,可以由 f i − 1 , j − 1 f_{i-1,j-1} f i − 1 , j − 1 不删除得到,也可以由 f i − 1 , j f_{i-1,j} f i − 1 , j 进行删除或不删除得到, 有三种可能,即
f i , j = min ( f i − 1 , j − 1 , f i − 1 , j , f i − 1 , j + p i ) f_{i,j}=\min(f_{i-1,j-1},f_{i-1,j},f_{i-1,j}+p_i)
f i , j = min ( f i − 1 , j − 1 , f i − 1 , j , f i − 1 , j + p i )
时空复杂度均为 O ( n m ) O(nm) O ( n m ) ,综合前面算法,期望得分 60 60 6 0 分。
算法五
我们发现算法四中的第二维其实可以去掉,因为我们可以马上知道 D P DP D P 到 { a i } \{a_i\} { a i } 中的第 i i i 位时 j j j 在哪里:我们设 f i f_i f i 表示考虑到了 { a i } \{a_i\} { a i } 的第 i i i 位的最小代价总和。为了减少边界条件的判断,我们在 { a i } \{a_i\} { a i } 和 { b i } \{b_i\} { b i } 后面再加一个哨兵点,权值为 n n n ,删除代价为 0 0 0 。 接着,我们按照 a i a_i a i 从小到大 D P DP D P :
若 a i ∉ { b j } a_i\notin \{b_j\} a i ∈ / { b j } ,则忽略;
若 a i ∈ { b j } a_i\in \{b_j\} a i ∈ { b j } ,则同算法二一样的:
f i = min x = 1 i − 1 ( f x + ∑ k = x + 1 i − 1 [ a k > b j − 1 ∨ p k < 0 ] p k ) [ a x = b j − 1 ] f_i=\min_{x=1}^{i-1}\left(f_x+\sum\limits_{k=x+1}^{i-1}{[a_k>b_{j-1}\vee p_k<0]p_k} \right)[a_x=b_{j-1}]
f i = x = 1 min i − 1 ( f x + k = x + 1 ∑ i − 1 [ a k > b j − 1 ∨ p k < 0 ] p k ) [ a x = b j − 1 ]
其中上面的和式中的东西可以拆成 a k > b j − 1 , p k ≥ 0 a_k>b_{j-1},p_k\ge 0 a k > b j − 1 , p k ≥ 0 与 p k < 0 p_k<0 p k < 0 两部分。后一部分可以同算法二一样用前缀和维护;而前一部分可以用树状数组维护。
时间复杂度为 O ( n 2 log n ) O(n^2\log{n}) O ( n 2 log n ) ,空间复杂度为 O ( n ) O(n) O ( n ) ,综合前面算法,期望得分 60 60 6 0 分。
算法六
我们发现算法四中的 a i < b j a_i<b_j a i < b j 与 a i > b j a_i>b_j a i > b j 两种情况的 j j j 必然是两个连续的区间,且都由 f i − 1 , j f_{i-1,j} f i − 1 , j 转移过来,并且二者所加的 0 0 0 以及 min ( 0 , p i ) \min(0, p_i) min ( 0 , p i ) 也都是常数,可以用区间加进行维护;而 a i = b j a_i=b_j a i = b j 的情况也只有一个点,单点修改即可。这些操作都可以用线段树或者树状数组来实现。
时间复杂度为 O ( n log m ) O(n\log{m}) O ( n log m ) ,空间复杂度为 O ( m ) O(m) O ( m ) ,期望得分 80 80 8 0 分。
算法七
我们发现算法五中 a i ∈ { b j } a_i\in \{b_j\} a i ∈ { b j } 的转移其实不需要枚举 x x x ,这个转移方程可以用广义的前缀最小值优化。什么意思呢?因为 f i f_i f i 只会从满足 a x = b j − 1 a_x=b_{j-1} a x = b j − 1 的 f x f_x f x 转移而来, 然后我们再改写一下状态转移方程:
f i = min x = 1 i − 1 ( f x + ∑ k = x + 1 i − 1 [ a k > b j − 1 ∧ p k ≥ 0 ] p k + ∑ k = x + 1 i − 1 [ p k < 0 ] p k ) [ a x = b j − 1 ] f_i=\min_{x=1}^{i-1}\left(f_x+\sum\limits_{k=x+1}^{i-1}{[a_k>b_{j-1}\land p_k\ge 0]p_k}+\sum\limits_{k=x+1}^{i-1}{[p_k<0]p_k} \right)[a_x=b_{j-1}]
f i = x = 1 min i − 1 ( f x + k = x + 1 ∑ i − 1 [ a k > b j − 1 ∧ p k ≥ 0 ] p k + k = x + 1 ∑ i − 1 [ p k < 0 ] p k ) [ a x = b j − 1 ]
我们记 s 1 x = ∑ k = 1 x [ a k > b j − 1 ∧ p k ≥ 0 ] p k , s 2 x = ∑ k = 1 x [ p k < 0 ] p k s1_x=\sum_{k=1}^x{[a_k>b_{j-1}\land p_k\ge 0]p_k},s2_x=\sum_{k=1}^x{[p_k<0]p_k} s 1 x = ∑ k = 1 x [ a k > b j − 1 ∧ p k ≥ 0 ] p k , s 2 x = ∑ k = 1 x [ p k < 0 ] p k ,那么又有:
f i = min x = 1 i − 1 ( f x + s 1 i − 1 − s 1 x + s 2 i − 1 − s 2 x ) f_i=\min_{x=1}^{i-1}(f_x+s1_{i-1}-s1_x+s2_{i-1}-s2_x)
f i = x = 1 min i − 1 ( f x + s 1 i − 1 − s 1 x + s 2 i − 1 − s 2 x )
我们再记一个数组 g y g_y g y ,并且假设当前我们要求的是 f k f_k f k ,那么:
g y = min x = 1 k − 1 ( f x − s 1 x − s 2 x ) [ a x = y ] g_y=\min_{x=1}^{k-1}(f_x-s1_x-s2_x)[a_x=y]
g y = x = 1 min k − 1 ( f x − s 1 x − s 2 x ) [ a x = y ]
这就是之前说的广义前缀最小值,意即所有满足 a x = y a_x=y a x = y 的 x x x 组成的子序列的前缀最小值。
又有了新的方程:
f i = g b j − 1 + s 1 i − 1 + s 2 i − 1 f_i=g_{b_{j-1}}+s1_{i-1}+s2_{i-1}
f i = g b j − 1 + s 1 i − 1 + s 2 i − 1
我们只需要在求出 f i f_i f i 后将 g a i g_{a_i} g a i 更新一下就可以做到每个点 O ( 1 ) O(1) O ( 1 ) 转移了。
其中 s 1 x s1_x s 1 x 还是需要树状数组来求,所以时间复杂度为 O ( n log n ) O(n\log{n}) O ( n log n ) ,空间复杂度为 O ( n ) O(n) O ( n ) ,综合前面算法期望得分 80 80 8 0 分。
算法八
发现复杂度的瓶颈在于求 s 1 x s1_x s 1 x ,我们考虑重构状态转移方程。
考虑贡献后移。我们称区间 ( b j − 1 , b j ] (b_{j-1},b_j] ( b j − 1 , b j ] 为“区间 j j j ”。在计算 f i f_i f i 时,我们可以只考虑满足 a k a_k a k 在“区间 j j j ”中的 k k k 的贡献,这是为什么呢?因为虽然从当前来看,并没把该删的都删了,比如说 a k > b j a_k>b_j a k > b j 的就没有删掉;但是从整体上来看,最后该删掉的就是 a k a_k a k 在“区间 1 1 1 ”、“区间 2 2 2 ”、⋯ \cdots ⋯ 、“区间 m + 1 m + 1 m + 1 ”的 k k k ,而计算完 f n f_n f n 时,我们已经将“区间 1 ∼ m + 1 1\sim m+1 1 ∼ m + 1 ”的都算完了,所以这样 D P DP D P 是不影响最后答案的。
我们预处理出每一个位置 k k k 它的 a k a_k a k 所在的“区间 q k q_k q k ”,实时维护 h l h_l h l 表示这时“区间 l l l ”要删掉的元素的代价总和,并且将 g y g_y g y 改为只需要记录 f x − s 2 x f_x-s2_x f x − s 2 x 的广义前缀最小值。那么就有新的方程:
f i = g b j − 1 + s 2 i − 1 + h j f_i=g_{b_{j-1}}+s2_{i-1}+h_j
f i = g b j − 1 + s 2 i − 1 + h j
直观来看求 q k q_k q k 是需要 lower_bound \texttt{lower\_bound} lower_bound 的,但是因为 { b i } \{b_i\} { b i } 单增,所以双指针就好了。
时空复杂度均为 O ( n ) O(n) O ( n ) ,期望得分 100 100 1 0 0 分。
标程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <bits/stdc++.h> #define il inline #define ll long long const int N=5e6 +5 ;int n,m,a[N],b[N],id[N],q[N]; ll f[N],g[N],h[N],p[N];il char gc () { static char buf[1 <<20 ],*ss,*tt; if (tt==ss) { tt=(ss=buf)+fread (buf,1 ,1 <<20 ,stdin); if (tt==ss) return EOF; } return *ss++; } il int read () { int res,sign=1 ; char c; while ((c=gc ())<'0' ||c>'9' ) if (c=='-' ) sign=-1 ; res=c^48 ; while ((c=gc ())>='0' &&c<='9' ) res=(res<<3 )+(res<<1 )+(c^48 ); return res*sign; } int main () { freopen ("offa.in" ,"r" ,stdin),freopen ("offa.out" ,"w" ,stdout); n=read (); int i,j; ll F,x=0 ; for (i=1 ; i<=n; i++) a[i]=read (); for (i=1 ; i<=n; i++) p[i]=read (); m=read (),n++,a[n]=n; for (i=1 ; i<=m; i++) id[b[i]=read ()]=i; m++,b[m]=n,id[n]=m,memset (g,63 ,8 *m+8 ),F=g[0 ],g[0 ]=0 ; for (i=j=1 ; i<=n; q[i++]=j) if (b[j]<i) j++; for (i=1 ; i<=n; i++) { j=id[a[i]]; if (j) f[i]=g[j-1 ]<F?g[j-1 ]+h[j]+x:F; if (p[i]>-1 ) h[q[a[i]]]+=p[i]; else x+=p[i]; if (j) (f[i]<F)&&(g[j]=std::min (g[j],f[i]-x)); } if (f[n]<F) printf ("%lld" ,f[n]); else puts ("Impossible" ); return 0 ; }