K 题

题解

在数组 中,每个给出的 前一定有一段 的子序列,不妨把这段子序列连续地放在 前,之后判断构造出的数组 是否合法。

数组 合法即 且不存在两个相邻的数后者与前者之差大于

之后,考虑 的位置,因为 是最小的,每次出现一定会将栈清空且无法被之后的数清空,所以 一定在最右的 处。

接下来考虑 的位置,因为此时 是剩下未使用过的数中最小的数,所以 会在次右的 或最右的 处。

……

重复以上步骤直至将 个数放完。

即,将构造好的数组 按值从小到大,位置从右至左的顺序依次赋上 即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    vector<int> b(n);
    vector<bool> vis(n);
    for (int i = 0; i < k; i++) {
        int p, x;
        cin >> p >> x;
        --p;
        b[p] = x;
        vis[p] = true;
    }
    for (int i = 0; i < n; i++) {
        if (vis[i]) { // 如果是给出的 b[i]
            for (int j = i - 1; j >= 0 and not vis[j]; j--) { // 将 1,2,...,b[i] - 1 连续地放在 b[i] 前
                b[j] = max(1, b[j + 1] - 1);
            }
        }
    }
    for (int i = 1; i < n; i++) { // 防止没给出 b[n - 1],因为之前只往左放
        if (b[i] == 0) {
            b[i] = b[i - 1];
        }
    }
    bool ok = b[0] == 1; 
    for (int i = 1; i < n; i++) { // 判断构造出的数组 b 是否合法
        if (b[i] - b[i - 1] > 1) {
            ok = false;
        }
    }
    if (not ok) {
        cout << -1 << "\n";
        return 0;
    }
    vector<int> p(n);
    iota(p.begin(), p.end(), 0);
    sort(p.begin(), p.end(), [&](int x, int y) { // 按值从小到大,位置从右至左的顺序排序
        return b[x] != b[y] ? b[x] < b[y] : x > y;
    });
    vector<int> ans(n);
    for (int i = 0; i < n; i++) { // 依次赋上 1,2,...,n
        ans[p[i]] = i + 1;
    }
    for (int i = 0; i < n; i++) {
        cout << ans[i] << " \n"[i == n - 1];
    }
    return 0;
}