Loading

区间 LIS

我们声称区间 LIS 可以做到 \(\mathcal{O}(n \log^2 n)\)。具体见 link1link2。但是这里给出的是 \(\mathcal{O}(n \sqrt{n} \log n)\) 的做法。

考虑 LIS 的求法,DP 显然很倒闭,另外一种是 \(f_i\) 表示 LIS 为 \(i\),结尾的数最小是多少,加入一个数 \(x\),则转移为 \(\forall f_i < x, f_{i + 1} \leftarrow \min(f_{i + 1}, x)\)。这就相当于,每次找到最小的 \(> x\) 的数,然后让 \(x\) 代替它的作用。

刻画一下这个过程,考虑一个初始为空的集合 \(S\),每次加入一个数 \(x\),如果 \(S\) 中没有比 \(x\) 更大的数,就让 \(S \leftarrow S \cup \{x\}\),否则将最小的 \(> x\) 的数修改为 \(x\)。则 LIS 就是最后 \(S\) 的大小。

设区间 \([l, r]\) 进行这个过程得到的集合为 \(S_{l, r}\),有如下性质:

  1. \(S_{l, r} \subseteq S_{l - 1, r}\)
  2. \(|S_{l - 1, r}| - |S_{l, r}| \le 1\)

证明是显然的,你考虑一开始令 \(S_{l, r} = \varnothing, S_{l - 1, r} = p_{l - 1}\),然后对 \([l, r]\) 进行这个过程,就发现是对的了。

那么 \(r\) 固定时,对于 \(x\),一定有一个前缀 \(l\) 满足 \(\forall i \le l, x \in S_{i, r}, \forall i > l, x \notin S_{i, r}\),记这个 \(l\)\(a_x\),那么询问就是问 \(a_x \ge l\)\(x\) 有多少个。考虑对 \(r\) 扫描线,\(r \rightarrow r + 1\) 时,记 \(v = p_{r + 1}\)\(a_v\) 显然会变为 \(r + 1\),且一开始有一个变量 \(x = 0\),对于 \(i = v + 1, v + 2, \dots, n\) 依次考虑,如果 \(a_i > x\)\(a_i\)\(x\) 交换,这就是模拟 \(S\) 中加了一个 \(v\) 后的改变。

然后发现这不是我们 P14400 吗。直接改改就过了,时间复杂度 \(\mathcal{O}(n \sqrt{n} \log n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 1e5 + 10, B = 317, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
    cout << arg << ' ';
    dbg(args...);
}
namespace Loop1st {
int n, Q, L[B], R[B], bel[N], cnt, p[N], a[N], ans[N];
priority_queue<int>q1[B];
priority_queue<int, vector<int>, greater<int>>q2[B];
struct Query {
    int l, r, id;
    bool operator < (const Query &A) const {
        return r < A.r;
    }
} q[N];
struct Fenwick {
    int tr[N];
    void add(int x, int v) {
        while (x) tr[x] += v, x -= x & -x;
    }
    int ask(int x) {
        int res = 0;
        while (x <= n) res += tr[x], x += x & -x;
        return res;
    }
} BIT;
void rebuild(int id) {
    auto &pq = q2[id];
    if (pq.empty()) return ;
    for (int i = L[id]; i <= R[id]; i++) if (a[i] > pq.top()) {
        pq.push(a[i]);
        a[i] = pq.top();
        pq.pop();
    }
    priority_queue<int, vector<int>, greater<int>>().swap(pq); // <=> pq.clear() 虽然 pq 没有 clear() 函数
}
int query(int l, int r, int x) {
    int bl = bel[l], br = bel[r];
    if (bl == br) {
        rebuild(bl);
        for (int i = l; i <= r; i++) if (a[i] > x) swap(a[i], x);
        priority_queue<int>(a + L[bl], a + R[bl] + 1).swap(q1[bl]); // 即令 q1[bl] = {a[L[bl]], a[L[bl] + 1], ..., a[R[bl]]}
        return x;
    }
    rebuild(bl); rebuild(br);
    for (int i = l; i <= R[bl]; i++) if (a[i] > x) swap(a[i], x);
    priority_queue<int>(a + L[bl], a + R[bl] + 1).swap(q1[bl]);
    for (int i = bl + 1; i < br; i++) {
        if (x >= q1[i].top()) continue;
        q2[i].push(x);
        q1[i].push(x);
        x = q1[i].top();
        q1[i].pop();
    }
    for (int i = L[br]; i <= r; i++) if (a[i] > x) swap(a[i], x);
    priority_queue<int>(a + L[br], a + R[br] + 1).swap(q1[br]);
    return x;
}
void main() {
    cin >> n >> Q;
    cnt = (n + B - 1) / B;
    for (int i = 1; i <= cnt; i++) {
        L[i] = R[i - 1] + 1;
        R[i] = i * B;
    }
    R[cnt] = n;
    for (int i = 1; i <= n; i++) {
        bel[i] = (i + B - 1) / B;
        cin >> p[i];
        q1[bel[i]].push(0);
    }
    for (int i = 1, l, r; i <= Q; i++) {
        cin >> l >> r;
        q[i] = {l, r, i};
    }
    sort(q + 1, q + Q + 1);
    // 0 永远不会被 ask 到,所以不用 BIT 维护 0
    for (int r = 1, now = 1; r <= n; r++) {
        int v = p[r];
        if (v < n) {
            int x = query(v + 1, n, 0);
            BIT.add(x, -1);
        }
        int b = bel[v];
        rebuild(b);
        BIT.add(a[v], -1); a[v] = r; BIT.add(a[v], 1);
        priority_queue<int>(a + L[b], a + R[b] + 1).swap(q1[b]);
        while (now <= Q && q[now].r == r) ans[q[now].id] = BIT.ask(q[now].l), now++;
    }
    for (int i = 1; i <= Q; i++) cout << ans[i] << '\n';
}

}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) Loop1st::main();
    return 0;
}
// start coding at 10:20
// finish debugging at 10:27

posted @ 2026-01-09 10:46  循环一号  阅读(6)  评论(0)    收藏  举报