题解:P7823 「RdOI R3」闹钟

· · 题解

简要题意

有两个变量 x,y,初始时有 x=y=0

每个时刻开始时你可以进行一次调整,一次调整可令一个变量增加或减少任意一个值,花费等量代价。

n 个时刻,每个时刻有一个整数 k_i,表示这个时刻结束时需要有至少一个变量等于 k_i

求花费的最小的代价和。

## 思路 设 $f(i,j)$ 表示在第 $i$ 个时刻,两个变量的值为 $k_i,j$ 的最小代价。 则不难有两种转移: $$ f(i,j)=\begin{cases} f(i-1,j)+|k_i-k_{i-1}|& j\neq k_{i-1}\\ \min\limits_{t=0}^{+\infty} f(i-1,t)+|k_i-t|& j=k_{i-1} \end{cases} $$ 简单解释一下转移的含义。第一种转移是让以前为 $k_{i-1}$ 的变量的值变成 $k_i$,而第二种转移是让以前不为 $k_{i-1}$ 的变量(或者两者均为 $k_{i-1}$ 的情况下其中的一个变量)的值变为 $k_i$。 我们发现第二种情况下,$j$ 的取值一定在 $k$ 中更优秀(我们不会无缘无故将 $j$ 变成一个毫不相干的数),我们将 $k$ 离散化作为 $j$ 这一维下标,这样可以做到 $O(n^2)$。难以通过本题,我们需要优化。 不难发现除了 $j=k_{i-1}$ 的位置外,其余的位置都是增加了一个定值 $|k_i-k_{i-1}|$。 而对于 $j=k_{i-1}$ 的情况,则是每个位置加上一个绝对值之后求全局最小值。 考虑绝对值我们应当怎么处理。可以将满足 $t\leq k_{i-1}$ 的所有位置加上 $k_i$ 减去 $t$。$t>k_{i-1}$ 同理。 加上 $k_{i-1}$ 相对好处理,不过减去 $t$ 就有难度。我们可以维护 $f'(i,j)=f(i,j)-j,f''(i,j)=f(i,j)+j$,这样子求最小值的时候可以拆成两个部分求最小值。 现在,我们需要找一个数据结构来维护 dp,需要支持区间加区间查询最小值,线段树可以胜任。 时间复杂度 $O(n\log n)$。 ## 代码 ```cpp #include <bits/stdc++.h> #define int long long #define ls (i << 1) #define rs (i << 1 | 1) #define mid ((l + r) >> 1) using namespace std; const int N = 1e5 + 5, inf = 1e18; int n, a[N], b[N], trs[N], rev[N]; struct sgt{ int t[N << 2], tag[N << 2]; void build(int i, int l, int r){ if(l == r) return t[i] = ((l == 1) ? 0 : inf), void(); build(ls, l, mid); build(rs, mid+1, r); t[i] = min(t[ls], t[rs]); } void mktag(int i, int v){ t[i] += v, tag[i] += v; } void pushdown(int i){ mktag(ls, tag[i]), mktag(rs, tag[i]), tag[i] = 0; } void update(int ql, int qr, int v, int i, int l, int r){ if(ql <= l && r <= qr) return mktag(i, v); pushdown(i); if(ql <= mid) update(ql, qr, v, ls, l, mid); if(mid < qr) update(ql, qr, v, rs, mid+1, r); t[i] = min(t[ls], t[rs]); } int query(int ql, int qr, int i, int l, int r){ if(ql > qr) return inf; if(ql <= l && r <= qr) return t[i]; pushdown(i); int res = inf; if(ql <= mid) res = min(res, query(ql, qr, ls, l, mid)); if(mid < qr) res = min(res, query(ql, qr, rs, mid+1, r)); return res; } void assign(int p, int v, int i, int l, int r){ if(l == r) return t[i] = v, void(); pushdown(i); if(p <= mid) assign(p, v, ls, l, mid); else assign(p, v, rs, mid+1, r); t[i] = min(t[ls], t[rs]); } } t, tp, tn; signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n; for(int i=1;i<=n;i++) cin >> a[i], b[i] = a[i]; int tot = n; b[++tot] = 0; sort(b+1, b+tot+1); tot = unique(b+1, b+n+1) - b; for(int i=1;i<=n;i++){ trs[i] = lower_bound(b+1, b+tot+1, a[i]) - b; rev[trs[i]] = i; } // cerr << tot << '\n'; // for(int i=1;i<=tot;i++) cerr << a[rev[i]] << ' '; // cerr << '\n'; trs[0] = 1, rev[1] = 0; t.build(1, 1, tot); tp.build(1, 1, tot); tn.build(1, 1, tot); for(int i=1;i<=n;i++){ int rt = min(tn.query(1, trs[i], 1, 1, tot) + a[i], tp.query(trs[i] + 1, tot, 1, 1, tot) - a[i]); t.update(1, tot, abs(a[i] - a[i - 1]), 1, 1, tot); tp.update(1, tot, abs(a[i] - a[i - 1]), 1, 1, tot); tn.update(1, tot, abs(a[i] - a[i - 1]), 1, 1, tot); t.assign(trs[i - 1], rt, 1, 1, tot); tp.assign(trs[i - 1], a[i - 1] + rt, 1, 1, tot); tn.assign(trs[i - 1], -a[i - 1] + rt, 1, 1, tot); // for(int j=1;j<=tot;j++) cerr << t.query(j, j, 1, 1, tot) << ' '; // cerr << '\n'; } cout << t.query(1, tot, 1, 1, tot) << '\n'; return 0; } // Written by xiezheyuan ```