LG8544 「Wdoi-2」禁断之门对面,是此世还是彼世【wqs 二分,DP】
非常好题目,爱来自中国!
首先发现 \(a\) 其实没啥用,把 \(a\) 提出来之后要最小化 \(b\) 的和。容易发现,如果构造出 \(B_1, B_2\) 使得前两行的答案最小,那么令 \(B_3, B_5, \cdots\) 取 \(B_1\),\(B_4, B_6, \cdots\) 取 \(B_2\) 就能得到 \(f(B)\) 的最小值。
于是只需要考虑前两行的情况,这等价于下面这个问题:
给定 \(c_1\sim c_m\),求 \(a_1\sim a_t\) 和 \(b_1\sim b_t\),使得 \(a_i,b_i\) 互不相同且 \(1\leq a_i,b_i \leq m\),且 \(a_i\neq b_i\),最小化 \(\sum\limits_{i = 1} ^ t c(a_i, b_i)\),其中 \(c(x, y)\) 表示 \(\sum\limits_{i = \min(x, y)} ^ {\max(x, y)} c_i\) 且 \(c_i > 0\)。
显然可以用费用流做,虽然过不去,但我们至少知道了以下事实:答案关于 \(t\) 的函数 \(f(t)\) 下凸。这是本题关键的性质。
先考虑 \(t = m\) 咋做,此时所有点都要匹配,我们建出一张 \(n\) 个点的图 \(G\),\(a_i \to b_i\) 连边,那么 \(G\) 由若干个环组成。容易得到如下结论:环之间两两不相交(这意味着每个换对应序列上的一段区间 \([l,r]\)),并且环 \([l,r]\) 的形态恰为 \(l \to l+1 \to \cdots \to r \to l\)。
当然还可以更近一步,原问题和 这题 拥有相似的形式,我们不妨猜测本题也有相似的结论。一个简单的观察是:使 \(|x-y|\) 太大是不优的。事实上确实如此,可以证明,任何长度 \(>4\) 的环都可以拆分成若干长度为 \(2\) 和 \(3\) 的环,并且答案不会更劣。具体证明分类讨论有点麻烦,这里就不写了)
于是直接 DP 就可以 \(O(m)\) 算了。
接下来考虑 \(t<m\) 的情况,容易发现图 \(G\) 上只会增加一些链,同样可以证明链的形态恰如 \(l\to l+1 \to \cdots \to r\)。沿用 \(t=m\) 时的 DP,设 \(f_{i,j,0/1}\) 表示前 \(i\) 个点有 \(j\) 对匹配,当前是否在某条继续延伸的链内,转移比较简单:
- \(f_{i,j,1} \gets f_{i-1,j-1,1} + c_{i-1}+c_i\) 表示继续延伸这条链。
- \(f_{i,j,1} \gets f_{i-2,j-1,0} + c_{i-1}+c_i\) 表示新开一条链。
- \(f_{i,j,0} \gets f_{i-2,j-2,0} + 2(c_{i-1} + c_i)\) 表示加一个长度为 \(2\) 的环。
- \(f_{i,j,0} \gets f_{i-3,j-3,0} + 2(c_{i-2} + c_i) + 3c_{i-1}\) 表示加一个长度为 \(3\) 的环。
- \(f_{i,j,0} \gets f_{i-1,j,0}\) 表示啥都不干。
- \(f_{i,j,0} \gets f_{i,j,1}\) 这一步在 \(f_{i,j,1}\) 转移之后执行,表示结束这条链。
这样可以做到 \(O(m^2)\)。
然后根据费用流我们能得到答案的凸性,于是直接套一个 wqs 二分就做完了。时间复杂度 \(O(m \log v)\)。
code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5, mod = 1e9 + 7;
const LL inf = 2e18;
int n, m, t; LL a[N], b[N], sum;
struct dat {
LL v, c;
dat (LL _v = 0, LL _c = 0) : v(_v), c(_c) {}
dat operator + (const dat &x) const { return {v + x.v, c + x.c}; };
bool operator < (const dat &x) const {
return v != x.v ? v < x.v : c < x.c;
}
} f[N][2];
int calc(LL mid) {
f[0][0] = f[1][0] = {0, 0};
f[0][1] = f[1][1] = {inf, 0};
for (int i = 2; i <= m; i++) {
f[i][0] = f[i - 1][0];
f[i][1] = min(f[i - 1][1], f[i - 2][0]) + dat(b[i] + b[i - 1] - mid, 1);
f[i][0] = min(f[i][0], f[i - 2][0] + dat(2 * (b[i] + b[i - 1]) - 2 * mid, 2));
if (i > 2) {
f[i][0] = min(f[i][0], f[i - 3][0] + dat(2 * (b[i] + b[i - 2]) + 3 * b[i - 1] - 3 * mid, 3));
}
f[i][0] = min(f[i][0], f[i][1]);
}
return f[m][0].c;
}
int main() {
ios :: sync_with_stdio(0);
cin >> n >> m >> t;
for (int i = 1; i <= n; i++) cin >> a[i], sum = (sum + a[i]) % mod;
for (int i = 1; i <= m; i++) cin >> b[i];
LL l = 1, r = 3e9, ret = 0;
while (l <= r) {
LL mid = (l + r) >> 1;
if (calc(mid) <= t) l = mid + 1, ret = mid;
else r = mid - 1;
}
calc(ret);
LL ans = (f[m][0].v + ret * t) % mod * sum % mod;
cout << ans << endl;
return 0;
}