哇马上区域赛了,感觉好多东西都没整理好。

整体二分。整体二分类似于一些决策单调性的分治,可以解决诸多区间第 k 小或区间第 k 大的问题。整体二分 solve(l,r,L,R) 表示答案在 [l,r] 中,与操作 [L,R] 有关(操作 [L,R] 不一定对应原来 [L,R] 的操作)

我们就拿静态区间第 k 小来说好了。如果原序列的数 ≤ mid ,那么就在树状数组中对应位置 +1 。如果碰到询问操作,那么查询询问区间 [ql,qr] 的值相当于查询了区间里面值在 [l,mid] 的个数,如果个数 ≤ k ,那么答案在 [mid+1,r] 中,那么把 k 减掉对应的个数,把操作分到右区间。否则答案在 [l,mid] 中,把操作分到左区间。如果 l=r ,那么直接把 ans 记录一下就好了。时间复杂度 O(n*log^2 n)

这里实现了一下基本的静态区间第k小,分别写了不离散化和离散化的版本,时间差个一半左右吧

另外有个小插曲是终于学到了负数意义下除2和右移结果不一样,除2是向0取整,右移是向负取整。

#include<bits/stdc++.h>

#define inf 1000000000
#define maxn 200010
using namespace std;
int n, m, num, a[maxn], s[maxn], ans[maxn];

struct node {
    int op, x, y, z, o;
} p[maxn * 2], q1[maxn * 2], q2[maxn * 2];

void Insert(int pos, int x) {
    while (pos <= n) {
        s[pos] += x, pos += pos & (-pos);
    }
}

int Query(int pos) {
    int res = 0;
    while (pos) {
        res += s[pos], pos -= pos & (-pos);
    }
    return res;
}

void Dfs(int l, int r, int L, int R) {
//    printf("%d %d %d %d\n", l, r, L, R);
    if (L > R)return;
    if (l == r) {
        for (int i = L; i <= R; i++)if (p[i].op == 2)ans[p[i].o] = l;
        return;
    }
    int p1 = 0, p2 = 0, mid = (l + r) >> 1;
    for (int i = L; i <= R; i++)
        if (p[i].op == 1) {
            if (p[i].x <= mid)Insert(p[i].o, 1), q1[++p1] = p[i];
            else q2[++p2] = p[i];
        } else {
            int cnt = Query(p[i].y) - Query(p[i].x - 1);
            if (cnt >= p[i].z)q1[++p1] = p[i];
            else p[i].z -= cnt, q2[++p2] = p[i];
        }
    for (int i = 1; i <= p1; i++)if (q1[i].op == 1)Insert(q1[i].o, -1);
    for (int i = 1; i <= p1; i++)p[L + i - 1] = q1[i];
    for (int i = 1; i <= p2; i++)p[L + i + p1 - 1] = q2[i];
    Dfs(l, mid, L, L + p1 - 1), Dfs(mid + 1, r, L + p1, R);
}

int main() {
    scanf("%d%d", &n, &m);
    int x, y, z;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x);
//        x = rand();
        p[++num] = node{1, x, 0, 0, i};
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &x, &y, &z);
//        x = rand() % n + 1, y = rand() % n + 1;
//        if (x > y)swap(x, y);
//        z = rand() % (y - x + 1) + 1;
        p[++num] = node{2, x, y, z, i};
    }
    Dfs(-inf, inf, 1, num);
    for (int i = 1; i <= m; i++)printf("%d\n", ans[i]);
    return 0;
}


/*离散化版本*/
#include<bits/stdc++.h>

#define maxn 200010
using namespace std;
int n, m, num, a[maxn], b[maxn], s[maxn], ans[maxn];

struct node {
    int op, x, y, z, o;
} p[maxn * 2], q1[maxn * 2], q2[maxn * 2];

void Insert(int pos, int x) {
    while (pos <= n) {
        s[pos] += x, pos += pos & (-pos);
    }
}

int Query(int pos) {
    int res = 0;
    while (pos) {
        res += s[pos], pos -= pos & (-pos);
    }
    return res;
}

void Dfs(int l, int r, int L, int R) {
    if (L > R)return;
    if (l == r) {
        for (int i = L; i <= R; i++)if (p[i].op == 2)ans[p[i].o] = b[l];
        return;
    }
    int p1 = 0, p2 = 0, mid = (l + r) >> 1;
    for (int i = L; i <= R; i++)
        if (p[i].op == 1) {
            if (p[i].x <= mid)Insert(p[i].o, 1), q1[++p1] = p[i];
            else q2[++p2] = p[i];
        } else {
            int cnt = Query(p[i].y) - Query(p[i].x - 1);
            if (cnt >= p[i].z)q1[++p1] = p[i];
            else p[i].z -= cnt, q2[++p2] = p[i];
        }
    for (int i = 1; i <= p1; i++)if (q1[i].op == 1)Insert(q1[i].o, -1);
    for (int i = 1; i <= p1; i++)p[L + i - 1] = q1[i];
    for (int i = 1; i <= p2; i++)p[L + i + p1 - 1] = q2[i];
    Dfs(l, mid, L, L + p1 - 1), Dfs(mid + 1, r, L + p1, R);
}

int main() {
    //freopen("132.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    int x, y, z;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    b[0] = unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= n; i++) {
        int pos = lower_bound(b + 1, b + 1 + b[0], a[i]) - b;
        p[++num] = node{1, pos, 0, 0, i};
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &x, &y, &z);
        p[++num] = node{2, x, y, z, i};
    }
    Dfs(1, b[0], 1, num);
    for (int i = 1; i <= m; i++)printf("%d\n", ans[i]);
    return 0;
}

还有动态的区间第k小,就是把修改操作拆成两个就好了。

#include<bits/stdc++.h>

#define inf 1000000000
#define maxn 100010
using namespace std;
int n, m, num, a[maxn], s[maxn], ans[maxn];

struct node {
    int op, x, y, z, o;
} p[maxn * 3], q1[maxn * 3], q2[maxn * 3];

void Insert(int pos, int x) {
    while (pos <= n) {
        s[pos] += x, pos += pos & (-pos);
    }
}

int Query(int pos) {
    int res = 0;
    while (pos) {
        res += s[pos], pos -= pos & (-pos);
    }
    return res;
}

void Dfs(int l, int r, int L, int R) {
    if (L > R)return;
    if (l == r) {
        for (int i = L; i <= R; i++)if (p[i].op == 2)ans[p[i].o] = l;
        return;
    }
    int p1 = 0, p2 = 0, mid = (l + r) >> 1;
    for (int i = L; i <= R; i++)
        if (p[i].op == 1) {
            if (p[i].x <= mid)Insert(p[i].o, p[i].y), q1[++p1] = p[i];
            else q2[++p2] = p[i];
        } else {
            int cnt = Query(p[i].y) - Query(p[i].x - 1);
            if (cnt >= p[i].z)q1[++p1] = p[i];
            else p[i].z -= cnt, q2[++p2] = p[i];
        }
    for (int i = 1; i <= p1; i++)if (q1[i].op == 1)Insert(q1[i].o, -q1[i].y);
    for (int i = 1; i <= p1; i++)p[L + i - 1] = q1[i];
    for (int i = 1; i <= p2; i++)p[L + i + p1 - 1] = q2[i];
    Dfs(l, mid, L, L + p1 - 1), Dfs(mid + 1, r, L + p1, R);
}

int main() {
    scanf("%d%d", &n, &m);
    int x, y, z;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        p[++num] = node{1, a[i], 1, 0, i};
    }
    char s[5];
    for (int i = 1; i <= m; i++) {
        scanf("%s", s);
        if (s[0] == 'Q') {
            scanf("%d%d%d", &x, &y, &z);
            p[++num] = node{2, x, y, z, i};
        } else {
            scanf("%d%d", &x, &y);
            p[++num] = node{1, a[x], -1, 0, x};
            a[x] = y;
            p[++num] = node{1, a[x], 1, 0, x};
        }
    }
    memset(ans, -1, sizeof(ans));
    Dfs(-inf, inf, 1, num);
    for (int i = 1; i <= m; i++)
        if (ans[i] != -1)printf("%d\n", ans[i]);
    return 0;
}
/*离散化 快20%的样子*/
#include<bits/stdc++.h>

#define inf 1000000000
#define maxn 100010
using namespace std;
int n, m, num, a[maxn], b[maxn * 3], s[maxn], ans[maxn];

struct node {
    int op, x, y, z, o;
} p[maxn * 3], q1[maxn * 3], q2[maxn * 3];
struct quer {
    char c;
    int x, y, z;
} q[maxn];

void Insert(int pos, int x) {
    while (pos <= n) {
        s[pos] += x, pos += pos & (-pos);
    }
}

int Query(int pos) {
    int res = 0;
    while (pos) {
        res += s[pos], pos -= pos & (-pos);
    }
    return res;
}

void Dfs(int l, int r, int L, int R) {
    if (L > R)return;
    if (l == r) {
        for (int i = L; i <= R; i++)if (p[i].op == 2)ans[p[i].o] = b[l];
        return;
    }
    int p1 = 0, p2 = 0, mid = (l + r) >> 1;
    for (int i = L; i <= R; i++)
        if (p[i].op == 1) {
            if (p[i].x <= mid)Insert(p[i].o, p[i].y), q1[++p1] = p[i];
            else q2[++p2] = p[i];
        } else {
            int cnt = Query(p[i].y) - Query(p[i].x - 1);
            if (cnt >= p[i].z)q1[++p1] = p[i];
            else p[i].z -= cnt, q2[++p2] = p[i];
        }
    for (int i = 1; i <= p1; i++)if (q1[i].op == 1)Insert(q1[i].o, -q1[i].y);
    for (int i = 1; i <= p1; i++)p[L + i - 1] = q1[i];
    for (int i = 1; i <= p2; i++)p[L + i + p1 - 1] = q2[i];
    Dfs(l, mid, L, L + p1 - 1), Dfs(mid + 1, r, L + p1, R);
}


int main() {
    //freopen("132.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    int x, y, z;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        b[++b[0]] = a[i];
    }
    char s[5];
    for (int i = 1; i <= m; i++) {
        scanf("%s", s);
        if (s[0] == 'Q') {
            scanf("%d%d%d", &x, &y, &z);
            q[i] = quer{s[0], x, y, z};
        } else {
            scanf("%d%d", &x, &y);
            q[i] = quer{s[0], x, y, 0};
            b[++b[0]] = y;
        }
    }
    sort(b + 1, b + 1 + b[0]);
    b[0] = unique(b + 1, b + 1 + b[0]) - b - 1;
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(b + 1, b + 1 + b[0], a[i]) - b;
        p[++num] = node{1, a[i], 1, 0, i};
    }
    for (int i = 1; i <= m; i++) {
        if (q[i].c == 'Q') {
            p[++num] = node{2, q[i].x, q[i].y, q[i].z, i};
        } else {
            x = q[i].x;
            y = lower_bound(b + 1, b + 1 + b[0], q[i].y) - b;
            p[++num] = node{1, a[x], -1, 0, x};
            a[x] = y;
            p[++num] = node{1, a[x], 1, 0, x};
        }
    }
    memset(ans, -1, sizeof(ans));
    Dfs(1, b[0], 1, num);
    for (int i = 1; i <= m; i++)
        if (ans[i] != -1)printf("%d\n", ans[i]);
    return 0;
}