QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: jiangly

Posted at: 2025-12-14 07:20:27

Last updated: 2025-12-14 07:20:46

Back to Problem

题解

题意

公司有 $N$ 个员工,每个人会在 $0$ 时刻到 $M$ 时刻之间外出一次。公司的门在内部可以随意开关,在外面必须要钥匙才能开关。第 $i$ 个人会在 $S_i$ 时刻离开并在 $T_i$ 时刻返回,且他只能在这两个时刻开关门。$0$ 时刻门是关闭的。公司有 $K$ 把钥匙,将它们分配给其中 $K$ 个员工,要求每个人在返回时都能进入公司,最大化门关闭的时间。

限制

$1\le N\le 2000$, $1\le M\le 10^9$, $1\le K< N$, $0< S_i< T_i< M$,所有 $S_i$, $T_i$ 两两不相等。

题解

考虑每相邻两个事件 $(l, r)$ 之间是否关门:

  • $(S_a,T_b)$:$a$ 和 $b$ 都有钥匙才能关门($a=b$ 时就是 $a$ 有钥匙);
  • $(S_a,S_b)$:$a$ 有钥匙才能关门;
  • $(T_a,T_b)$:$b$ 有钥匙才能关门;
  • $(T_a,S_b)$:一定会关门。

把每个人看作一个点,选择每个点会贡献一个权值;同时存在若干条边,同时选择边的两个端点也会贡献一个权值。问题变为求解不超过 $K$ 个点的最大权导出子图。显然每个点的度不超过 $2$ 且无环,即图是若干条链。简单 DP 即可。

时间复杂度:$O(N^2)$。

代码

#include <iostream>
#include <vector>
#include <algorithm>
constexpr int N = 2'000;
struct Event {
    int t, i, o;
    Event() = default;
    Event(int t, int i, int o) : t(t), i(i), o(o) {}
    friend bool operator<(const Event &lhs, const Event &rhs) {
        return lhs.t < rhs.t;
    }
};
int n, m, k, cnt;
Event event[2 * N + 2];
int val[N + 1], nxt[N + 1], vNext[N + 1], p[N + 1];
bool vis[N + 1];
int dp[2][N + 1][2];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cin >> n >> m >> k;
    std::fill(nxt, nxt + n + 1, -1);
    event[cnt++] = {0, n, 0};
    event[cnt++] = {m, n, 1};
    for (int i = 0; i < n; ++i) {
        int s, t;
        std::cin >> s >> t;
        event[cnt++] = {s, i, 0};
        event[cnt++] = {t, i, 1};
    }
    std::sort(event, event + cnt);
    int ans = 0;
    for (int i = 0; i <= 2 * n; ++i) {
        auto &&[l, a, t0] = event[i];
        auto &&[r, b, t1] = event[i + 1];
        int t = 2 * t0 + t1, v = r - l;
        if (t == 0) {
            val[a] += v;
        } else if (t == 1) {
            if (a == b) {
                val[a] += v;
            } else {
                nxt[b] = a;
                vNext[b] = v;
                vis[a] = true;
            }
        } else if (t == 2) {
            ans += v;
        } else {
            val[b] += v;
        }
    }
    cnt = 0;
    for (int i = 0; i <= n; ++i)
        if (!vis[i])
            for (int j = i; j != -1; j = nxt[j])
                p[cnt++] = j;
    int cur = 0;
    for (int i = 0; i <= k + 1; ++i)
        dp[0][i][0] = dp[0][i][1] = -1;
    dp[0][1][1] = val[p[0]];
    dp[0][0][0] = 0;
    for (int i = 1; i <= n; ++i) {
        cur ^= 1;
        for (int j = 0; j <= k + 1; ++j)
            dp[cur][j][0] = dp[cur][j][1] = -1;
        for (int j = 0; j <= k + 1 && j <= i + 1; ++j)
            for (int x = 0; x <= 1; ++x)
                for (int y = 0; y <= 1; ++y)
                    if (j >= y && dp[cur ^ 1][j - y][x] != -1)
                        dp[cur][j][y] = std::max(dp[cur][j][y], dp[cur ^ 1][j - y][x] + y * val[p[i]] + x * y * vNext[p[i - 1]]);
    }
    std::cout << ans + dp[cur][k + 1][1] << "\n";
    return 0;
}

Comments

No comments yet.