「 Luogu P2801 」 教主的魔法——分块

# 解题思路

修改,就是一个区间修改的常规操作,但是为了迎合查询的需要,对两端的不完整的块需要暴力重构,重新进行排序操作,保证每一块都是单调上升的顺序。

然后再说进行查询的操作,起初,我们需要在每一个块内进行排序。保证顺序时单调上升的(在每一个块内,是独立的),然后查询的时候对每一块($k$)都二分查找到大于 $num + tag[k]$ 的第一个数的位置,然后就可以得到一共有多少大于 $num$ 的数了。

值得注意的是,有些题解写的是对原序列直接进行排序,这就有一个错误,那就是在排序之后序列的顺序改变,改变了之后在进行区间修改的时候就不能保证正确性了。

所以我们开一个 vector 将每个块内的数都存下来。我的代码 T 了一个点,但是了 O2 过了,懒得再去卡常了

 

# 附上代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 1e6+3;
int N, Q, arr[maxn], in[maxn], cnt, tag[maxn];
vector<int> b[1003];
struct BLOCK {
    template <typename T> inline void read(T &x) {
        x = 0; T f = 1; char c = getchar();
        while (c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
        while (c <= '9' && c >= '0') {x = x*10 + c-'0'; c = getchar();}
        x *= f;
    }
    void reset(int k) {
        b[k].clear();
        for(int i=(k-1)*cnt+1; i<=min(k*cnt, N); i++)
            arr[i] += tag[k], b[k].push_back(arr[i]);
        sort(b[k].begin(), b[k].end());
        tag[k] = 0;
    }
    void build() {
        cnt = sqrt(N);
        for(int i=1; i<=N; i++) {
            read(arr[i]);
            in[i] = (i-1)/cnt+1;
            b[in[i]].push_back(arr[i]);
        }
        for(int i=in[1]; i<=in[N]; i++)
            sort(b[i].begin(), b[i].end());
    }
    void update(int l, int r, int num) {
        for(int i=l; i<=min(in[l]*cnt, r); i++)
            arr[i] += num;
        reset(in[l]);
        if(in[l] != in[r]) {
            for(int i=(in[r]-1)*cnt+1; i<=r; i++)
                arr[i] += num;
            reset(in[r]);
        }
        for(int i=in[l]+1; i<in[r]; i++)
            tag[i] += num;
    }
    int check(int k, int num) {
        int ans = (b[k].end()-lower_bound(b[k].begin(), b[k].end(), num-tag[k]));
        return ans;
    }
    int query(int l, int r, int num) {
        int ans = 0;
        for(int i=l; i<=min(in[l]*cnt, r); i++)
            if(arr[i] + tag[in[i]] >= num) ans ++;
        if(in[l] != in[r]) {
            for(int i=(in[r]-1)*cnt+1; i<=r; i++)
                if(arr[i] + tag[in[i]] >= num) ans ++;
        }
        for(int i=in[l]+1; i<in[r]; i++)
            ans += check(i, num);
        return ans;
    }
    BLOCK () {
        read(N), read(Q);
        build();
        char opt;
        int l, r, num;
        for(int i=1; i<=Q; i++) {
            cin>>opt;
            read(l), read(r), read(num);
            if(opt == 'M') update(l, r, num);
            else printf("%d\n", query(l, r, num));
        }
    }
}BLO;
int main() {}

 

posted @ 2018-10-15 11:22  Mystical-W  阅读(153)  评论(0编辑  收藏  举报