[COCI2012-2013#2] INFORMACIJE 題解

XuYueming發表於2024-08-30

前言

題目連結:洛谷

題意簡述

你需要構造一個 \(1 \sim n\) 的排列 \(a\),滿足 \(m\) 個條件,格式如下:

  • 1 x y v\(\max \limits _ {i = l} ^ r a_i = v\)

  • 2 x y v\(\min \limits _ {i = l} ^ r a_i = v\)

題目分析

首先這個最值很難受,考慮能不能轉化成我們喜歡的二元關係。比如,當 \([l, r]\) 的最值為 \(v\) 時,說明 \(v\) 必須出現在這段區間內。再思考一下,對於最大值,為了保證這段區間的最大值是 \(v\),那麼大於 \(v\) 的數就不能出現在這段區間內,最小值同理。把前者“必須出現”轉化為補集的“不能出現”,這樣我們處理出了對於每一個值 \(v\) 不能出現的位置。

有什麼用呢?我在膜尼賽上用爆搜騙分。直接騙肯定不好看,這時候就需要一些常見的剪枝最佳化。對於限制類題目,我們把限制多的先搜尋能節約不少時間。此外,尋找到一個沒有確定的位置可以使用雙向連結串列最佳化。預處理區間覆蓋轉化為差分。能夠得到 \(86\) 分的好成績

話說回來,這麼搜肯定是錯的,那還有什麼做法呢?我們必須敏感地注意到位置和值的特殊二元關係。到了最後,每一個位置一定一一對應一個值,將“不能”反轉得到若干“可能”的匹配關係,最後的對應一定這些可能匹配中的一種。

不妨繼續抽象模型。有紅黃兩種顏色的小球各 \(n\) 個,我們知道了每一個紅球可能和哪些黃球匹配,找到一種匹配方式,使得紅黃球一一對應。這不就是二分圖的裸題了嗎?我們將可能得匹配方式看作是值向位置的連邊,最終的答案就是位置對應的那個值。

注意到存在無解的情況,此時說明二分圖不存在完美匹配,判斷即可。

時間複雜度理論上來說可以最佳化到:\(\Theta(m \log n + n^{\frac{5}{2}})\),但是可能比不過常數小的 \(\Theta(nm + n ^ 3)\)

程式碼

#include <cstdio>

#define isdigit(x) ('0' <= x && x <= '9')
inline void read(int &x) {
    x = 0; char ch = getchar();
    for (; !isdigit(ch); ch = getchar());
    for (;  isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
}

const int N = 220;

int n, m, val[N], mi[N], mx[N], cant[N][N];
int to[N * N], nxt[N * N], head[N], tot;
int vis[N], timer, mark[N];

bool dfs(int now) {
    for (int i = head[now]; i; i = nxt[i]) {
        int to = ::to[i];
        if (vis[to] != timer) {
            vis[to] = timer;
            if (!mark[to] || dfs(mark[to])) {
                mark[to] = now;
                return true;
            }
        }
    }
    return false;
}

signed main() {
    read(n), read(m);
    for (int i = 1; i <= n; ++i) mx[i] = n;
    for (int i = 1, op, l, r, v; i <= m; ++i) {
        read(op), read(l), read(r), read(v);
        ++cant[v][1], --cant[v][l];
        ++cant[v][r + 1], --cant[v][n + 1];
        for (int j = l; j <= r; ++j) {
            if (op == 1)
                v < mx[j] && (mx[j] = v);
            else
                v > mi[j] && (mi[j] = v);
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cant[i][j] += cant[i][j - 1];
            if (mi[j] <= i && i <= mx[j] && !cant[i][j]) {
                to[++tot] = j;
                nxt[tot] = head[i];
                head[i] = tot;
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        ++timer;
        if (!dfs(i)) return puts("-1"), 0;
    }
    for (int i = 1; i <= n; ++i) printf("%d ", mark[i]);
    return 0;
}

總結 & 反思

說實話,都想到爆搜,想不到二分圖匹配確實不應該,應該歸根結底是二分圖模型不熟悉了。

二分圖是處理兩種不同型別的物件之間的對應關係。對於本題,一個排列的構造,我們如果能找出值和下標的二元關係,就能建立二分圖,跑匹配,最後輸出匹配的方案。