LOJ 3313 「ZJOI2020」序列

题意

一个长度为 n n n 的序列,你每次可以执行下面三个操作中的一种:

  1. 选择一个区间,把区间中的数字 − 1 -1 1
  2. 选择一个区间,把区间中编号为奇数的数字 − 1 -1 1
  3. 选择一个区间,把区间中编号为偶数的数字 − 1 -1 1

问至少需要多少次操作可以把序列全部变成 0.

n ≤ 100000 , T ≤ 10 n \le 100000,T \le 10 n100000,T10

题解

不知道有没有什么高妙的贪心或者结论,反正 LP 对偶可以暴力做……(如果有高妙做法请留言教教我 /kel)

简化版

为了方便,我们先考虑只有操作 1 时怎么 LP。

想一会儿,可以发现定义 s i s_i si 表示 1 ∼ i 1 \sim i 1i 中有多少操作区间的左端点, 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 sisi1titi1sitisiti1aiti1siais0z,t0zminimize: snz

注意到最大费用循环流的对偶是:

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} pipj+vi,jcosti,jminimize: vi,jcapi,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 sisi1+00titi1+00siti+00siti1+0aiti1si+0ais0z,t0zzsn+v0minimize: v

所以连边如下:

s i s_i si s i − 1 s_{i-1} si1 连容量无穷大,费用为 0 0 0 的边;
t i t_i ti t i − 1 t_{i-1} ti1 连容量无穷大,费用为 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} ti1 连容量无穷大,费用为 a i a_i ai 的边;
t i − 1 t_{i-1} ti1 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 si1,si 之间, t i − 1 , t i t_{i-1},t_i ti1,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(fi1,0,fi1,1ai)
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(fi1,0+ai,fi1,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=21i=0naiai+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 sisi1titi1sitisisi2titi2sitisiti1+siti2aiti1si+ti2siais0,t0,s0,t0,s1,t1zminimize: snz+snz

大部分限制都一样很好处理,但是这两行不能直接对偶成费用流了:

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 \\ siti1+siti2aiti1si+ti2siai

我刚开始一直以为这就没法做了,后来发现其实是可以做的……

考虑 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 siti1+siti2ai 的对偶是什么,这就相当于 s i s_i si t i − 1 t_{i-1} ti1 连容量无穷大,费用为 a i a_i ai 的边, s i ′ s'_i si t i − 2 ′ t'_{i-2} ti2 连容量无穷大,费用为 0 0 0 的边,并且要求这两条边经过的流量相等(因为等价于把一条边加入四个点的流量守恒式中)。

类似的,因为这个图长得很有性质,所以冷静分析一下还是可以做的。

我们还是考虑只有操作 1 时的图,操作 2,3 实际上是在那张图上走的路径多加了一些限制。我们定义 i − 1 {i-1} i1 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

  1. 任意两个相邻的贡献点不能同时贡献正数。
  2. 把编号为偶数的贡献点删掉,剩下的数列中相邻的贡献点不能同时贡献正数。
  3. 把编号为奇数的贡献点删掉,剩下的数列中相邻的贡献点不能同时贡献正数。

于是可以 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;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值