QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: pystraf

Posted at: 2026-08-03 22:39:10

Last updated: 2026-08-03 22:40:59

Back to Problem

Editorial for #6400

Description

给定数轴上的坐标升序的 $n$ 个点,坐标为 $a_{1\sim n}$,点有权值 $b_{1\sim n}$。

还给出一个区间 $[l,r]$,从点 $1$ 出发,每次跳 $u\to v$ 必须满足 $l\le a_v-a_u\le r$,要通过若干次操作跳到 $n$。

将走过点的 $b_i$ 非升序排成一个序列,最大化其字典序,若无解输出 $-1$,$t$ 组数据。

Limitations

$1\le \sum n\le 10^6,1\le l\le r\le 10^9$

$1\le a_i\le 10^9,1\le b_i\le n$

$2.5\text{s},1024\text{MB}$

Solution

先考虑暴力,设 $S_i$ 表示走到 $i$ 时的最优序列,维护字典序升序的单调队列 $Q$。

双指针维护 DP,持续推进 $j$ 直到 $a_i-a_j\ge l$ 不满足,其中对每个 $j$ 先弹出 $Q$ 队头所有 $S_j>S_\text{front}$ 的序列,然后从 $Q$ 队头压入 $j$ 保证单调性。

然后若 $Q$ 队尾不满足 $a_i-a_\text{back}\le r$,则不断弹掉队尾直到满足,显然此时 $l\le a_i-a_\text{back}\le r$ 一定满足,且 $\text{back}$ 位置是最优的,直接令 $S_i=S_\text{back}\cup {b_i}$ 即可。

由于单次比较序列要 $O(n)$,总复杂度 $O(n^2)$。但是你发现可以用权值线段树维护 hash 值 $\sum_{x=1}^n B^x\text{cnt}_x$,容易二分来 $O(\log n)$ 比较两个序列。由于转移是复制再插入,改成 PST 即可,复杂度 $O(n\log n)$,注意记录 $S_i$ 为空的情况。

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
    if(a < b){ a = b; return true; }
    return false;
}

template<class T>
bool chmin(T &a, const T &b){
    if(a > b){ a = b; return true; }
    return false;
}

constexpr int B = 1313131;

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t; cin >> t;
    while (t--) {
        int n, L, R;
        cin >> n >> L >> R;

        vector<int> a(n), b(n);
        for (int i = 0; i < n; i++) cin >> a[i];
        for (int i = 0; i < n; i++) cin >> b[i], b[i]--;

        vector<ui64> pw(n + 1); pw[0] = 1;
        for (int i = 0; i < n; i++) {
            pw[i + 1] = pw[i] * B;
        }

        struct Node {
            int l, r;
            int cnt; ui64 hs;
        };
        vector<Node> t(1); t.reserve(n * 40);
        function<int(int, int, int, int)>
        insert = [&](int pre, int l, int r, int x) {
            int u = t.size();
            t.push_back(t[pre]);
            t[u].hs += pw[x], t[u].cnt++;
            if (l == r) return u;

            const int mid = (l + r) >> 1;
            if (x <= mid)
                t[u].l = insert(t[pre].l, l, mid, x);
            else
                t[u].r = insert(t[pre].r, mid + 1, r, x);
            return u;
        };

        function<bool(int, int, int, int)>
        compare = [&](int u, int v, int l, int r) {
            if (l == r) return t[u].cnt > t[v].cnt;

            const int mid = (l + r) >> 1;
            if (t[t[u].r].hs == t[t[v].r].hs)
                return compare(t[u].l, t[v].l, l, mid);
            else
                return compare(t[u].r, t[v].r, mid + 1, r);
        };

        vector<int> ans;
        function<void(int, int, int)>
        flat = [&](int u, int l, int r) {
            if (!u) return;
            if (l == r) {
                for (int i = 0; i < t[u].cnt; i++)
                    ans.push_back(l);
                return;
            }

            const int mid = (l + r) >> 1;
            flat(t[u].r, mid + 1, r);
            flat(t[u].l, l, mid);
        };

        vector<int> root(n);
        vector<bool> vis(n); deque<int> dq;
        root[0] = insert(0, 0, n - 1, b[0]);

        for (int i = 1, j = -1; i < n; i++) {
            while (j + 1 < i && a[i] - a[j + 1] >= L) {
                j++; if (vis[j]) continue;
                while (!dq.empty() && compare(
                       root[j], root[dq.front()], 0, n - 1))
                    dq.pop_front();
                dq.push_front(j);
            }
            while (!dq.empty() && a[i] - a[dq.back()] > R) dq.pop_back();
            if (dq.empty()) vis[i] = true;
            else
                root[i] = insert(root[dq.back()], 0, n - 1, b[i]);
        }

        if (vis[n - 1]) cout << "-1\n";
        else {
            flat(root[n - 1], 0, n - 1);
            cout << ans.size() << '\n';
            for (int x : ans) cout << x + 1 << ' ';
            cout << '\n';
        }
    }

    return 0;
}

Comments

No comments yet.