• HDU 5412 CRB and Queries (整体二分)


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5412

    题目大意:对一个数组A[N](N<= 10^5)进行两种操作:

      1 l v:将第A[l]的值修改成v

      2 l r k:求l~r区间的第k小值

    操作总数为10^5.

    2015年多校第十场的题目,当时乱套主席树什么的模板,发现并不能过,赛后学习了一种叫做整体二分的方法,感觉很是厉害。

    整体二分是二分答案。大致方法如下:

      1、先把所有对数组的操作保存起来,包括赋值。

      2、从0~inf二分一个数值,并以此数值为界,将操作划分成两部分

      3、递归处理

    细节有点不好说,还是看代码比较清楚。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define N 150100
    #define inf 1000000100
    struct Que{
        int tp;
        int x, y, k, id, cur;
    }Q[N<<1], Q1[N<<1], Q2[N<<1];
    int qcnt;
    int a[N];
    
    int tr[N];
    int ans[N];
    int m;
    inline void add(int id, int v){
        for(int i = id; i < N; i += (i&-i))
            tr[i] += v;
    }
    inline int sum(int id){
        int ret = 0;
        for(int i = id; i > 0; i -= (i&-i))
            ret += tr[i];
        return ret;
    }
    
    int tmp[N];
    void divide(int s, int t, int l, int r)
    {
        if(s > t) return;
        if(l == r)
        {
            for(int i = s; i <= t; i++)
                if(Q[i].tp == 2)
                    ans[Q[i].id] = l;
            return;
        }
        int mid = (l+r)>>1;
        for(int i = s; i <= t; i++)
        {
            if(Q[i].tp == 0 && Q[i].y <= mid) add(Q[i].x, 1);
            else if(Q[i].tp == 1 && Q[i].y <= mid) add(Q[i].x, -1);
            else if(Q[i].tp == 2) tmp[Q[i].id] = sum(Q[i].y) - sum(Q[i].x-1);
        }
        //还原 
        for(int i = s; i <= t; i++)
        {
            if(Q[i].tp == 0 && Q[i].y <= mid) add(Q[i].x, -1);
            else if(Q[i].tp == 1 && Q[i].y <= mid) add(Q[i].x, 1);
        }
        //下面是根据当前二分的答案mid,将操作划分成两部分 
        int l1 = 0, l2 = 0;
        for(int i = s; i <= t; i++)
        {
            if(Q[i].tp == 2){
                if(Q[i].cur+tmp[Q[i].id] >= Q[i].k) Q1[l1++] = Q[i];
                else {
                    Q[i].cur += tmp[Q[i].id];
                    Q2[l2++] = Q[i];
                }
            }
            else{
                if(Q[i].y <= mid) Q1[l1++] = Q[i];
                else Q2[l2++] = Q[i];
            }
        }
        for(int i = 0; i < l1; i++) Q[s+i] = Q1[i];
        for(int i = 0; i < l2; i++) Q[s+l1+i] = Q2[i];
        divide(s, s+l1-1, l, mid);
        divide(s+l1, t, mid+1, r);
    }
    
    int main()
    {
        int n, q;
        while(~scanf("%d", &n))
        {
            qcnt = 0;
            int Max = -1;
            int Min = inf;
            for(int i = 1; i <= n; i++){
                scanf("%d", &a[i]);
                Max = max(Max, a[i]);
                Min = min(Min, a[i]);
                //赋值也要看成是操作 
                Q[qcnt].x = i, Q[qcnt].y = a[i], Q[qcnt].tp = 0;
                Q[qcnt++].cur = 0;
            }
            scanf("%d", &q);
            int id = 0;
            for(int i = 0; i < q; i++)
            {
                int op, x, y, k;
                scanf("%d", &op);
                if(op == 1){
                    scanf("%d %d", &x, &y);
                    //to == 1表示这个值是要被修改的,减去当前值,再加上新值,就完成了修改 
                    //cur的作用:若当前的答案二分区间是 [l, r],则cur保存在Q[i].l, Q[i].r范围之内,有多少个小于 l 的数 
                    Q[qcnt].x = x, Q[qcnt].y = a[x], Q[qcnt].tp = 1, Q[qcnt++].cur = 0;
                    Q[qcnt].x = x, Q[qcnt].y = y, Q[qcnt].tp = 0, Q[qcnt++].cur = 0;
                    a[x] = y;
                    Max = max(Max, y);
                    Min = min(Min, y);
                }
                else {
                    scanf("%d %d %d", &x, &y, &k);
                    Q[qcnt].x = x, Q[qcnt].y = y, Q[qcnt].k = k, Q[qcnt].tp = 2;
                    Q[qcnt].id = id++, Q[qcnt++].cur = 0;
                }
            }
            //树状数组每次add后还会被还原,就没必要每次初始化了 
            //memset(tr, 0, sizeof(tr));
            divide(0, qcnt-1, Min, Max);
            for(int i = 0; i < id; i++)
                printf("%d
    ", ans[i]);
        }
        return 0;
    }
  • 相关阅读:
    java之集合Collection详解之3
    委托的高级使用
    委托的一般使用
    泛型(Generic)委托
    泛型(Generic)方法(函数,算法)
    泛型(Generic)接口
    泛型(Generic)类的使用原因和使用方式
    C#反射从入门到放弃(这部分遇到的新东西太多了让人接受不能)
    反射应用——依赖注入
    接口的显式实现
  • 原文地址:https://www.cnblogs.com/beisong/p/4756829.html
Copyright © 2020-2023  润新知