整体二分 hdu5412 CRB and Queries

传送门:点击打开链接

题意:两种操作,操作1查询区间第k大,操作2把位置x的值改成y。n,Q<=1e5

思路:整体二分。刚开始以为是在线段树上二分答案,没想到它竟然是对所有的二分答案,还是第一次见到这样的二分方法!

说下整体二分的大概思路:

首先,按照操作顺序,将所有的查询和修改操作全部加入到数组A中,然后去调用solve函数,L和R其实是数组A的指针,l和r是二分答案的范围

之后,取l和r的中点m,然后顺序遍历数组A中所有的操作,该修改的在树状数组上修改,该查询的在树状数组上查询。

之后,把数组A给划分开,划分成前l1部分的操作位置的数都<=m,后l2部分的操作位置的数都>m,划分完后,分开递归,并缩小答案范围。

最后总复杂度是,整体二分的复杂度是O(nlogn),树状数组的复杂度是O(logn),合起来一共是O(nlogn^2)

还有一个技巧,就是树状数组上直接打标记,来快速清空数组

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 4e5 + 5;
const int INF = 0x3f3f3f3f;

int sum[MX], flag[MX], n, DFN;
int val[MX], ans[MX], tmp[MX];
struct Data {
    int op, id, x, y, k, cnt;
    Data() {}
    Data(int _op, int _id, int _x, int _y, int _k) {
        op = _op; id = _id;
        x = _x; y = _y; k = _k;
    }
} A[MX], T1[MX], T2[MX];
void add(int x, int y, int id) {
    for(int i = x; i <= n; i += i & -i) {
        if(flag[i] != id) flag[i] = id, sum[i] = 0;
        sum[i] += y;
    }
}
int ask(int x, int id) {
    int ret = 0;
    for(int i = x; i; i -= i & -i) {
        if(flag[i] == id) ret += sum[i];
    }
    return ret;
}
void solve(int L, int R, int l, int r) {
    if(L > R) return;
    if(l == r) {
        for(int i = L; i <= R; i++) {
            if(A[i].op == 3) ans[A[i].id] = l;
        }
        return;
    }

    int m = (l + r) >> 1; DFN++;
    for(int i = L; i <= R; i++) {
        if(A[i].op == 1 && A[i].y <= m) add(A[i].x, 1, DFN);
        if(A[i].op == 2 && A[i].y <= m) add(A[i].x, -1, DFN);
        if(A[i].op == 3) tmp[i] = ask(A[i].y, DFN) - ask(A[i].x - 1, DFN);
    }
    int l1 = 0, l2 = 0;
    for(int i = L; i <= R; i++) {
        if(A[i].op == 3) {
            if(A[i].cnt + tmp[i] >= A[i].k) T1[++l1] = A[i];
            else A[i].cnt += tmp[i], T2[++l2] = A[i];
        } else if(A[i].y <= m) T1[++l1] = A[i];
        else T2[++l2] = A[i];
    }
    for(int i = 1; i <= l1; i++) A[L + i - 1] = T1[i];
    for(int i = 1; i <= l2; i++) A[L + l1 + i - 1] = T2[i];
    solve(L, L + l1 - 1, l, m);
    solve(L + l1, R, m + 1, r);
}

int main() {
    //FIN;
    while(~scanf("%d", &n)) {
        int sz = 0, Q, qsz = 0, Max = 0; DFN = 0;
        memset(sum, 0, sizeof(sum));

        for(int i = 1; i <= n; i++) {
            scanf("%d", &val[i]);
            A[++sz] = Data(1, -1, i, val[i], -1);
            Max = max(Max, val[i]);
        }
        scanf("%d", &Q);
        for(int i = 1; i <= Q; i++) {
            int op, x, y, k;
            scanf("%d%d%d", &op, &x, &y);
            if(op == 1) {
                A[++sz] = Data(2, -1, x, val[x], -1);
                A[++sz] = Data(1, -1, x, y, -1);
                Max = max(Max, y);
                val[x] = y;
            } else {
                qsz++;
                scanf("%d", &k);
                A[++sz] = Data(3, qsz, x, y, k);
                A[sz].cnt = 0;
            }
        }
        solve(1, sz, 0, Max);

        for(int i = 1; i <= qsz; i++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值