美文网首页
蓝桥杯-2014-B组-10-小朋友排队(拓展树状数组模板)

蓝桥杯-2014-B组-10-小朋友排队(拓展树状数组模板)

作者: 御史神风 | 来源:发表于2021-04-08 21:20 被阅读0次

    使用到了普通的树状数组和拓展的树状数组。
    普通的只能单点修改和区间查询,利用两次区间查询可以做到单点查询。如果要区间修改时间复杂度是O(n)。
    拓展树状数组可以区间修改和区间查询,利用两次区间操作即可实现单点操作,但增加了一个额外的数组。
    题目思路是用一个普通bit记录该点是否未排序。一个拓展bit记录被排序次数。

    1. 然后模拟插入排序,选取最小点,该点左边的点被排序次数都+1(已排序的点可能会被再次累加,但不影响,原因见第4步)。
    2. 该点的被排序次数+n,n显然等于该点左边未排序的点的数量。
    3. 接着更新该点的未排序标志。
    4. 接着该点将不会参与后续排序,所以可以直接累加该点;由于后续不会再使用该点的被累加次数,所以再次执行第1步时,错误累加该点的被排序次数不会对结果造成影响。
    #pragma warning(disable:4996)
    #include <stdio.h>
    #include <stdlib.h>
    #include <utility>
    #include <algorithm>
    
    using namespace std;
    
    typedef unsigned long long ull;
    
    int isDebug = 0;
    
    pair<int, int> childs[20]; // hight, index
    int unhappy[20 + 1]; // 拓展bit 记录被排序次数
    int unhappy2[20 + 1];
    int counts[20 + 1]; // 普通bit 记录该点是否未排序
    
    // 普通bit 单点修改 bit[i] += x
    void bitAdd(int* bit, int i, int x)
    {
        while (i < 21) {
            bit[i] += x;
            i += i & -i;
        }
    }
    
    // 拓展bit 区间修改 bit[i, N] += x
    void bitAdd(int* bit1, int* bit2, int i, int x)
    {
        int ii = i;
        while (i < 21) {
            bit1[i] += x;
            bit2[i] += x * (ii - 1);
            i += i & -i;
        }
    }
    
    // 普通bit 区间查询 前i项和 
    int bitSum(int* bit, int i)
    {
        int s = 0;
        while (i > 0) {
            s += bit[i];
            i -= i & -i;
        }
        return s;
    }
    
    // 拓展bit 区间查询 前i项和
    int bitSum(int* bit1, int* bit2, int i)
    {
        int s = 0;
        int ii = i;
    
        while (i > 0) {
            s += ii * bit1[i] - bit2[i];
            i -= i & -i;
        }
    
        return s;
    }
    
    // 遍历单点查询
    void debug()
    {
        if (!isDebug)
            return;
    
        int i, last = 0, now = 0;
        for (i = 1; i < 6; i++) {
            now = bitSum(unhappy, unhappy2, i);
            printf("%d\n", now - last);
            last = now;
        }
        printf("\n");
    }
    
    int main(void)
    {
        freopen("q.txt", "r", stdin);
    
        int n, N, h, index;
        int last = 0, now = 0, ans = 0;
    
        scanf("%d", &N);
    
        for (n = 0; n < N; n++) {
            scanf("%d", &h);
            childs[n].first = h;
            childs[n].second = n + 1;
        }
    
        sort(childs, childs+n);
    
        for (n = 1; n < 21; n++)
            bitAdd(counts, n, 1);
    
        debug();
        for (n = 1; n < 21; n++)
            bitAdd(unhappy, unhappy2, n, 0);
        debug();
    
        for (n = 0; n < N; n++) {
            index = childs[n].second;
    
            // 1 o[1, index - 1] += 1
            bitAdd(unhappy, unhappy2, 1, 1);
            debug();
            bitAdd(unhappy, unhappy2, index, -1);
            debug();
    
            // 2 o[index] += index - n - 1
            int leftCount = bitSum(counts, index-1);
            bitAdd(unhappy, unhappy2, index, leftCount);
            debug();
            bitAdd(unhappy, unhappy2, index + 1, -leftCount);
            debug();
    
            // 3 计数数组中删除该节点
            bitAdd(counts, index, -1);
            // printf("%d %d\n", index, bitSum(unhappy, unhappy2, index) - bitSum(unhappy, unhappy2, index - 1));
            // 4
            index = bitSum(unhappy, unhappy2, index) - bitSum(unhappy, unhappy2, index - 1);
            ans += (1 + index) * index / 2;
        }
    
        printf("%d\n", ans);
    
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:蓝桥杯-2014-B组-10-小朋友排队(拓展树状数组模板)

          本文链接:https://www.haomeiwen.com/subject/ietpkltx.html