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;   
}
posted @ 2022-10-17 09:45  came11ia  阅读(42)  评论(0编辑  收藏  举报