奥法之劫(offa)题解

本人第一次出五校,出的就是这题。

奥法之劫 (offa)

题目信息

时间限制:1800 ms1800 \texttt{ ms}
空间限制:512 MiB512 \texttt{ MiB}
本题使用文件输入输出。
输入文件名:offa.in
输出文件名:offa.out

题目背景

你很清楚地知道一颗小小的鹅卵石也会引起山崩。同样地,一次单纯的背叛居然引发 了魔法瘟疫,而其后果还在托瑞尔上狂舞肆虐着,且远不止于此。
– 阴影谷之伊尔明斯特,于 1479 DR,永恒者之年

术士:终于到了么,因为奥法之疫而形成的幽暗深渊。

战士:还没能看见深渊的时候,我们就碰到了不少疫变生物,感觉这一路上凶多 吉少啊。

游侠:感想就之后再说吧,现在已经黄昏了,我们得赶紧找扎帐篷的地方了。

术士:确实。嗯……七点钟方向的峡谷中有不少的废弃要塞,我们可以过去看看。

题目描述

游侠:大致勘察了一下。这个峡谷是 U 形的,只有向西一个口子。而其中有 nn 座废弃要塞,从西向东第 ii 座要塞的编号为 ii,高度是 aia_i

术士:啧。这就有点尴尬了,我的想法是,给出一个长度为 mm 的高度序列 {bi}\{b_i\}, 满足 1i<m,bi<bi+1\forall 1\le i<m,b_i<b_{i+1},然后选择这样的 mm 座要塞满足:从西向东的编号分别为 q1,q2,,qmq_1,q_2,\cdots,q_m,对于所有的 1im1\le i\le maqi=bia_{q_i}=b_i,并且没有要塞向西的视线会被挡住, 即对于选出的每一座要塞,向西方向的其他要塞的高度都会严格小于这座要塞的高度。这样子高度非常合适。如果疫变生物来袭,可以有更佳的作战环境。但因为从远处看看不真切,忽略了除这 mm 座要塞之外的会挡住视线的情况。

战士:或许我们可以把其他的 nmn-m 座要塞拆掉,这样既可以搜刮这些要塞中的资源,又可以解决视线问题。这件事有术士你的身体强化法术应该并不是难事。

游侠:我觉得这不太现实,就算有身体强化,凭我们仨,也要拆到深夜,这样耽误了休息时间,还有可能在拆除的时候被疫变生物攻击。

术士:我倒觉得这不失为一个办法,但是我们可以更聪明一些。我们只需要拆掉一部分要塞,使得最后从其中可以选出 mm 座要塞,从西向东的第 ii 座要塞的高度要和 bib_i 相同,并且选出的要塞都不会被挡住向西的视线。这样便可以解决问题了,也不需要花费太多时间。

战士:我觉得上面的条件可以再加上一个条件:除了选出的 mm 座要塞,其他未被拆除的要塞向西的视线都要被挡住,不能给敌人留下任何有视线开阔的要塞。

游侠:我也说一下我的想法吧。根据我勘察的结果,拆除第 ii 座要塞的代价是 pip_i, 我们要尽量让拆除代价总和最小。

术士:那我来做一个最后总结吧,现在我们要解决的问题描述如下:

nn 座废弃要塞,从西向东第 ii 座要塞的高度为 aia_i,拆除这座要塞的代价为 pip_i。 现在我们给定一个长度为 mm 的高度序列 {bi}\{b_i\},满足
1i<m,bi<bi+1\forall 1\le i<m,b_i<b_{i+1}。然后我们想要拆掉一部分要塞,使得最后从其中可以选出 mm 座要塞,从西向东的编号分别是 q1,q2,,qmq_1,q_2,\cdots,q_m,满足:

  1. 对于任意的 1im1\le i\le maqi=bia_{q_i}=b_i
  2. 选出的要塞向西的视线都不会被挡住;
  3. 除了选出的 mm 座要塞,其他未被拆除的要塞向西的视线都会被挡住。 最后要最小化拆除代价总和。

战士:嗯。就是这样了。

游侠:那么我们马上开工吧。

输入格式

从文件 offa.in 中读入数据。

输入的第一行包含两个正整数 n,mn,m,表示荒野中废弃要塞的数量,以及术士想要保留的废弃要塞的数量。

接下来一行,包含 nn 个整数,第 ii 个整数为 aia_i,表示从西向东第 ii 座废弃要塞的高度。

再接下来一行,包含 nn 个整数,第 ii 个整数为 pip_i,表示从西向东第 ii 座拆除废弃要塞的代价。

再接下来一行,包含 mm 个整数,第 ii 个整数为 bib_i,表示术士想要保留的从西向东第 ii 座废弃要塞的高度。

输出格式

输出到文件 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
0

样例解释 1

我们拆除第 4,6,7,8,9,10,114,6,7,8,9,10,11 座要塞,选取第 1,2,51,2,5 座要塞,这样拆除总代价为 75+0+32+10+1=0−7 − 5 + 0 + 3 − 2 + 10 + 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

1
Impossible

样例解释 2

在原来的要塞中,不存在高度为 44 的要塞,所以不管怎么拆要塞都不能满足术士的要求。

样例 3

见选手目录下的 offa/offa3.inoffa/offa3.ans

本样例满足 Subtask #2 的限制。

样例 4

见选手目录下的 offa/offa4.inoffa/offa4.ans

本样例满足 Subtask #3 的限制。

样例 5

见选手目录下的 offa/offa5.inoffa/offa5.ans

本样例满足 Subtask #4 的限制。

样例 6

见选手目录下的 offa/offa6.inoffa/offa6.ans

数据范围与提示

对于所有的数据,1mn5×1061\le m\le n\le 5\times 10^61ai,bin1\le a_i,b_i\le npi109|p_i|\le 10^9,且保证 1i<m,bi<bi+1\forall 1\le i<m,b_i<b_{i+1}

Subtask #1 (13 points)n20n\le 20
Subtask #2 (27 points)n1000n\le 1000
Subtask #3 (10 points)m=1m=1
Subtask #4 (10 points){ai}\{a_i\}1n1\sim n 的排列。
Subtask #5 (20 points)n500000n\le 500000
Subtask #6 (20 points):无特殊性质。

提示:本题输入输出规模较大,请注意选择较为高效的输入方式(下发文件中的 offa/fast_input.cpp 可以拷贝使用)。

题解

原题链接

CF1334F Strange Function

数据范围有加强,加强思路来自题解下面的讨论区。

简要题意

定义两个序列之间的函数 ff:给出一个长度为 ll 的序列 {xi}\{x_i\}f({xi})f(\{x_i\}) 为所有满足 1il,xi>xi1,xi>xi2,,xi>x11\le i\le l,x_i>x_{i-1},x_i>x_{i-2},\cdots,x_i>x_1xix_i 按照原序列次序组成的新序列。

给定一个长度为 nn 的序列 {ai}\{a_i\} 和一个长度为 mm 的序列 {bi}\{b_i\},在序列 {ai}\{a_i\} 中删除第 ii 个数的代价为 pip_i,求最小的删除代价,使得 f({ai})={bi}f(\{a_i\})=\{b_i\},或者给出无解。

算法一

{bi}\{b_i\} 不是 {ai}\{a_i\} 的子序列则无解。否则枚举每个数删或者不删,最后判断取最小代价。

时间复杂度为 O(2nm)O(2^nm),空间复杂度为 O(n)O(n),期望得分 1313 分。

算法二

考虑 m=1m=1 的情况,现在我们已经知道了要留下哪一个值。我们求出所有 ax=b1a_x=b_1xx,我们考虑如果 f({ai})f\left(\{a'_i\}\right) 留下的位置只有 xx 的话序列 {ai}\{a'_i\} 要满足什么条件。很显然的,要满足 a1=b1a'_1=b_1,且 a1a'_1 要为全局最大值。

那么我们枚举 xx,它的答案就是:

i=1x1pi+i=x+1n[ai>b1pi<0]pi\sum\limits_{i=1}^{x-1} {p_i}+\sum\limits_{i=x+1}^n{[a_i>b_1\vee p_i<0]p_i}

两个和式可以分别用前、后缀和来维护,于是我们可以做到 O(1)O(1) 计算当前位置的答案

时空复杂度均为 O(n)O(n),综合前面算法,期望总得分 2323 分。

算法三

考虑 {ai}\{a_i \} 是一个排列的情况,现在我们已经知道了要留下那些位置。设 ax=bi,ay=bi+1a_x=b_i,a_y=b_{i+1},我们考虑 𝑥 与 𝑦 之间要删那些数。很显然,与算法二一样,aj>bia_j>b_i 或 者 pj<0p_j<0 的位置要删。那么我们直接模拟一遍就好了,还要注意 b1b_1 之前和 bmb_m 之后 的该删的也要删掉。

时空复杂度均为 O(n)O(n),综合前面算法,期望总得分 3333 分。

算法四

考虑一个 DPDP,设 fi,jf_{i,j} 表示我们考虑到了 {ai}\{a_i\} 的第 ii 位,{bj}\{b_j\}jj 位的最小代价总和。

转移方程分 aI<bj,ai>bj,ai=bja_I<b_j,a_i>b_j,a_i=b_j 三种情况:

ai<bja_i<b_j,第 ii 项元素可以删除也可以不删除,因为该元素不会加入新数组,对结果不产生影响,可以得到

fi,j=fi1,j+min(0,pi)f_{i,j}=f_{i-1,j}+\min(0,p_i)

ai>bja_i>b_j,则 aia_i 必须删除,因此

fi,j=fi1,j+pif_{i,j}=f_{i-1,j}+p_i

ai=bja_i=b_j,可以由 fi1,j1f_{i-1,j-1} 不删除得到,也可以由 fi1,jf_{i-1,j} 进行删除或不删除得到, 有三种可能,即

fi,j=min(fi1,j1,fi1,j,fi1,j+pi)f_{i,j}=\min(f_{i-1,j-1},f_{i-1,j},f_{i-1,j}+p_i)

时空复杂度均为 O(nm)O(nm),综合前面算法,期望得分 6060 分。

算法五

我们发现算法四中的第二维其实可以去掉,因为我们可以马上知道 DPDP{ai}\{a_i\} 中的第 ii 位时 jj 在哪里:我们设 fif_i 表示考虑到了 {ai}\{a_i\} 的第 ii 位的最小代价总和。为了减少边界条件的判断,我们在 {ai}\{a_i\}{bi}\{b_i\} 后面再加一个哨兵点,权值为 nn,删除代价为 00。 接着,我们按照 aia_i 从小到大 DPDP

ai{bj}a_i\notin \{b_j\},则忽略;

ai{bj}a_i\in \{b_j\},则同算法二一样的:

fi=minx=1i1(fx+k=x+1i1[ak>bj1pk<0]pk)[ax=bj1]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}]

其中上面的和式中的东西可以拆成 ak>bj1,pk0a_k>b_{j-1},p_k\ge 0pk<0p_k<0 两部分。后一部分可以同算法二一样用前缀和维护;而前一部分可以用树状数组维护。

时间复杂度为 O(n2logn)O(n^2\log{n}),空间复杂度为 O(n)O(n),综合前面算法,期望得分 6060 分。

算法六

我们发现算法四中的 ai<bja_i<b_jai>bja_i>b_j 两种情况的 jj 必然是两个连续的区间,且都由 fi1,jf_{i-1,j} 转移过来,并且二者所加的 00 以及 min(0,pi)\min(0, p_i) 也都是常数,可以用区间加进行维护;而 ai=bja_i=b_j 的情况也只有一个点,单点修改即可。这些操作都可以用线段树或者树状数组来实现。

时间复杂度为 O(nlogm)O(n\log{m}),空间复杂度为 O(m)O(m),期望得分 8080 分。

算法七

我们发现算法五中 ai{bj}a_i\in \{b_j\} 的转移其实不需要枚举 xx,这个转移方程可以用广义的前缀最小值优化。什么意思呢?因为 fif_i 只会从满足 ax=bj1a_x=b_{j-1}fxf_x 转移而来, 然后我们再改写一下状态转移方程:

fi=minx=1i1(fx+k=x+1i1[ak>bj1pk0]pk+k=x+1i1[pk<0]pk)[ax=bj1]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}]

我们记 s1x=k=1x[ak>bj1pk0]pk,s2x=k=1x[pk<0]pks1_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},那么又有:

fi=minx=1i1(fx+s1i1s1x+s2i1s2x)f_i=\min_{x=1}^{i-1}(f_x+s1_{i-1}-s1_x+s2_{i-1}-s2_x)

我们再记一个数组 gyg_y,并且假设当前我们要求的是 fkf_k,那么:

gy=minx=1k1(fxs1xs2x)[ax=y]g_y=\min_{x=1}^{k-1}(f_x-s1_x-s2_x)[a_x=y]

这就是之前说的广义前缀最小值,意即所有满足 ax=ya_x=yxx 组成的子序列的前缀最小值。

又有了新的方程:

fi=gbj1+s1i1+s2i1f_i=g_{b_{j-1}}+s1_{i-1}+s2_{i-1}

我们只需要在求出 fif_i 后将 gaig_{a_i} 更新一下就可以做到每个点 O(1)O(1) 转移了。

其中 s1xs1_x 还是需要树状数组来求,所以时间复杂度为 O(nlogn)O(n\log{n}),空间复杂度为 O(n)O(n),综合前面算法期望得分 8080 分。

算法八

发现复杂度的瓶颈在于求 s1xs1_x,我们考虑重构状态转移方程。

考虑贡献后移。我们称区间 (bj1,bj](b_{j-1},b_j] 为“区间 jj ”。在计算 fif_i 时,我们可以只考虑满足 aka_k 在“区间 jj ”中的 kk 的贡献,这是为什么呢?因为虽然从当前来看,并没把该删的都删了,比如说 ak>bja_k>b_j 的就没有删掉;但是从整体上来看,最后该删掉的就是 aka_k 在“区间 11 ”、“区间 22 ”、\cdots、“区间 m+1m + 1 ”的 kk,而计算完 fnf_n 时,我们已经将“区间 1m+11\sim m+1 ”的都算完了,所以这样 DPDP 是不影响最后答案的。

我们预处理出每一个位置 kk 它的 aka_k 所在的“区间 qkq_k ”,实时维护 hlh_l 表示这时“区间 ll ”要删掉的元素的代价总和,并且将 gyg_y 改为只需要记录 fxs2xf_x-s2_x 的广义前缀最小值。那么就有新的方程:

fi=gbj1+s2i1+hjf_i=g_{b_{j-1}}+s2_{i-1}+h_j

直观来看求 qkq_k 是需要 lower_bound\texttt{lower\_bound} 的,但是因为 {bi}\{b_i\} 单增,所以双指针就好了。

时空复杂度均为 O(n)O(n),期望得分 100100 分。

标程

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;
}