题意
一个长度为 n n n 的序列,你每次可以执行下面三个操作中的一种:
- 选择一个区间,把区间中的数字 − 1 -1 −1。
- 选择一个区间,把区间中编号为奇数的数字 − 1 -1 −1。
- 选择一个区间,把区间中编号为偶数的数字 − 1 -1 −1。
问至少需要多少次操作可以把序列全部变成 0.
n ≤ 100000 , T ≤ 10 n \le 100000,T \le 10 n≤100000,T≤10
题解
不知道有没有什么高妙的贪心或者结论,反正 LP 对偶可以暴力做……(如果有高妙做法请留言教教我 /kel)
简化版
为了方便,我们先考虑只有操作 1 时怎么 LP。
想一会儿,可以发现定义 s i s_i si 表示 1 ∼ i 1 \sim i 1∼i 中有多少操作区间的左端点, t i t_i ti 表示有多少操作区间的右端点,那么有如下限制:
s i ≥ s i − 1 t i ≥ t i − 1 s i ≥ t i s i − t i − 1 ≥ a i t i − 1 − s i ≥ − a i s 0 ≥ z , t 0 ≥ z minimize: s n − z s_i \ge s_{i-1} \\ t_i \ge t_{i-1} \\ s_i \ge t_i \\ s_i-t_{i-1} \ge a_i \\ t_{i-1}-s_i \ge -a_i \\ s_0 \ge z,t_0 \ge z \\ \text{minimize:\ }s_n-z si≥si−1ti≥ti−1si≥tisi−ti−1≥aiti−1−si≥−ais0≥z,t0≥zminimize: sn−z
注意到最大费用循环流的对偶是:
p i − p j + v i , j ≥ c o s t i , j minimize: v i , j ⋅ c a p i , j p_i-p_j+v_{i,j} \ge cost_{i,j} \\ \text{minimize:\ }v_{i,j} \cdot cap_{i,j} pi−pj+vi,j≥costi,jminimize: vi,j⋅capi,j
所以稍微变换一下形式:
s i − s i − 1 + 0 ≥ 0 t i − t i − 1 + 0 ≥ 0 s i − t i + 0 ≥ 0 s i − t i − 1 + 0 ≥ a i t i − 1 − s i + 0 ≥ − a i s 0 ≥ z , t 0 ≥ z z − s n + v ≥ 0 minimize: v s_i - s_{i-1} + 0 \ge 0 \\ t_i - t_{i-1} + 0 \ge 0 \\ s_i - t_i + 0 \ge 0 \\ s_i - t_{i-1} + 0 \ge a_i \\ t_{i-1} - s_i + 0 \ge -a_i \\ s_0 \ge z,t_0 \ge z \\ z - s_n + v \ge 0 \\ \text{minimize:\ }v si−si−1+0≥0ti−ti−1+0≥0si−ti+0≥0si−ti−1+0≥aiti−1−si+0≥−ais0≥z,t0≥zz−sn+v≥0minimize: v
所以连边如下:
s
i
s_i
si 到
s
i
−
1
s_{i-1}
si−1 连容量无穷大,费用为
0
0
0 的边;
t
i
t_i
ti 到
t
i
−
1
t_{i-1}
ti−1 连容量无穷大,费用为
0
0
0 的边;
s
i
s_i
si 到
t
i
t_i
ti 连容量无穷大,费用为
0
0
0 的边;
s
i
s_i
si 到
t
i
−
1
t_{i-1}
ti−1 连容量无穷大,费用为
a
i
a_i
ai 的边;
t
i
−
1
t_{i-1}
ti−1 到
s
i
s_i
si 连容量无穷大,费用为
−
a
i
-a_i
−ai 的边;
s
0
,
t
0
s_0,t_0
s0,t0 到
z
z
z 分别连容量无穷大,费用为
0
0
0 的边;
z
z
z 到
s
n
s_n
sn 连容量为
1
1
1,费用为
0
0
0 的边。
求上图的最大费用循环流即为答案。注意到上图的性质很特殊,可以 d p dp dp 求解费用流。具体的:
f i , 0 / 1 f_{i,0/1} fi,0/1 表示当前 d p dp dp 到 s i − 1 , s i s_{i-1},s_i si−1,si 之间, t i − 1 , t i t_{i-1},t_i ti−1,ti 之间,经过的上一条费用不为 0 0 0 的边费用是负的/正的。那么显然:
f
i
,
0
=
max
(
f
i
−
1
,
0
,
f
i
−
1
,
1
−
a
i
)
f_{i,0}=\max(f_{i-1,0},f_{i-1,1}-a_i)
fi,0=max(fi−1,0,fi−1,1−ai)
f
i
,
1
=
max
(
f
i
−
1
,
0
+
a
i
,
f
i
−
1
,
1
)
f_{i,1}=\max(f_{i-1,0}+a_i,f_{i-1,1})
fi,1=max(fi−1,0+ai,fi−1,1)
不难发现 f i , 1 = f i , 0 + a i f_{i,1}=f_{i,0}+a_i fi,1=fi,0+ai,所以归纳可证 f n , 1 = 1 2 ∑ i = 0 n ∣ a i − a i + 1 ∣ f_{n,1}=\frac{1}{2}\sum_{i=0}^{n}|a_i-a_{i+1}| fn,1=21∑i=0n∣ai−ai+1∣( a n + 1 a_{n+1} an+1 视为 0 0 0),这与结论是相符的。
推广到原题
现在考虑加入操作 2 , 3 2,3 2,3,类似的,限制变成了如下:
s i ≥ s i − 1 t i ≥ t i − 1 s i ≥ t i s i ′ ≥ s i − 2 ′ t i ′ ≥ t i − 2 ′ s i ′ ≥ t i ′ s i − t i − 1 + s i ′ − t i − 2 ′ ≥ a i t i − 1 − s i + t i − 2 ′ − s i ′ ≥ − a i s 0 , t 0 , s 0 ′ , t 0 ′ , s − 1 ′ , t − 1 ′ ≥ z minimize: s n − z + s n ′ − z s_i \ge s_{i-1} \\ t_i \ge t_{i-1} \\ s_i \ge t_i \\ s'_i \ge s'_{i-2} \\ t'_i \ge t'_{i-2} \\ s'_i \ge t'_i \\ s_i-t_{i-1}+s'_i-t'_{i-2} \ge a_i \\ t_{i-1}-s_i+t'_{i-2}-s'_i \ge -a_i \\ s_0,t_0,s'_0,t'_0,s'_{-1},t'_{-1} \ge z \\ \text{minimize:\ }s_n-z+s'_n-z si≥si−1ti≥ti−1si≥tisi′≥si−2′ti′≥ti−2′si′≥ti′si−ti−1+si′−ti−2′≥aiti−1−si+ti−2′−si′≥−ais0,t0,s0′,t0′,s−1′,t−1′≥zminimize: sn−z+sn′−z
大部分限制都一样很好处理,但是这两行不能直接对偶成费用流了:
s i − t i − 1 + s i ′ − t i − 2 ′ ≥ a i t i − 1 − s i + t i − 2 ′ − s i ′ ≥ − a i s_i-t_{i-1}+s'_i-t'_{i-2} \ge a_i \\ t_{i-1}-s_i+t'_{i-2}-s'_i \ge -a_i \\ si−ti−1+si′−ti−2′≥aiti−1−si+ti−2′−si′≥−ai
我刚开始一直以为这就没法做了,后来发现其实是可以做的……
考虑 s i − t i − 1 + s i ′ − t i − 2 ′ ≥ a i s_i-t_{i-1}+s'_i-t'_{i-2} \ge a_i si−ti−1+si′−ti−2′≥ai 的对偶是什么,这就相当于 s i s_i si 到 t i − 1 t_{i-1} ti−1 连容量无穷大,费用为 a i a_i ai 的边, s i ′ s'_i si′ 到 t i − 2 ′ t'_{i-2} ti−2′ 连容量无穷大,费用为 0 0 0 的边,并且要求这两条边经过的流量相等(因为等价于把一条边加入四个点的流量守恒式中)。
类似的,因为这个图长得很有性质,所以冷静分析一下还是可以做的。
我们还是考虑只有操作 1 时的图,操作 2,3 实际上是在那张图上走的路径多加了一些限制。我们定义 i − 1 {i-1} i−1 到 i i i 为第 i i i 个贡献点,则第 i i i 个贡献点可以贡献 a i , 0 , − a i a_i,0,-a_i ai,0,−ai 三种值,并且有一些限制,要求贡献总和最大。
定义两个贡献点是相邻的,当且仅当他们两之间的贡献点贡献的都是 0 0 0。
- 任意两个相邻的贡献点不能同时贡献正数。
- 把编号为偶数的贡献点删掉,剩下的数列中相邻的贡献点不能同时贡献正数。
- 把编号为奇数的贡献点删掉,剩下的数列中相邻的贡献点不能同时贡献正数。
于是可以 dp 了,记 f i , 000 / 001 / 010 / 101 / 110 / 111 f_{i,000/001/010/101/110/111} fi,000/001/010/101/110/111 表示前 i i i 个贡献点,前面第一个不为 0 0 0 的贡献点贡献的是负/正,前面第一个编号为奇数的不为 0 0 0 的贡献点贡献的是负/正,前面第一个编号为偶数的不为 0 0 0 的贡献点贡献的是负/正。
每个状态都会转移到两种状态,和当前位置的奇偶性有关。直接 d p dp dp 即可,复杂度 O ( n ) O(n) O(n)。具体转移见代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template<typename T> void chkmax(T &a, const T &b) { a = a > b ? a : b; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 100005;
const LL INF = 1e18;
int v[MAXN], n, T;
int main() {
for (scanf("%d", &T); T--;) {
LL a000 = 0, a001 = -INF, a101 = -INF, a010 = -INF, a110 = -INF, a111 = -INF;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", v + i);
for (int i = n; i > 0; i--) {
LL b000 = a000, b001 = a001, b101 = a101, b010 = a010, b110 = a110, b111 = a111;
if (i & 1) {
chkmax(a110, b000 + v[i]);
chkmax(a111, b001 + v[i]);
chkmax(a001, b101 - v[i]);
chkmax(a000, b010 - v[i]);
chkmax(a000, b110 - v[i]);
chkmax(a001, b111 - v[i]);
} else {
chkmax(a101, b000 + v[i]);
chkmax(a000, b001 - v[i]);
chkmax(a000, b101 - v[i]);
chkmax(a111, b010 + v[i]);
chkmax(a010, b110 - v[i]);
chkmax(a010, b111 - v[i]);
}
// printf("%lld %lld %lld %lld %lld %lld\n", a000, a001, a101, a010, a110, a111);
}
printf("%lld\n", max(max(max(a000, a001), a101), max(max(a010, a110), a111)));
}
return 0;
}