P2801 分块 + 二分

题意

传送门 P2801 教主的魔法

题解

考虑分块,对于整块的元素,排序后,便可以二分答案;对于左右边界不足一块的元素,朴素修改、统计,并对所在块重新进行排序。设块数为 t t t,由于分块规模与 N N N 只有指数上的差异,将 log ⁡ ( N / t ) \log(N/t) log(N/t) 近似为 O ( log ⁡ N ) O(\log N) O(logN),那么初始化排序 O ( N log ⁡ N ) O(N\log N) O(NlogN),单次查询 O ( N / t + t log ⁡ N ) O(N/t+t\log N) O(N/t+tlogN),单次修改 O ( N / t + N / t log ⁡ N + t ) O(N/t+N/t\log N+t) O(N/t+N/tlogN+t);当分块大小取 N \sqrt N N ,满足 N ≈ Q ( N / t + t ) N\approx Q(N/t+t) NQ(N/t+t)

需要注意的是,排序会修改元素位置,故应保留原始数组;每次排序,将数组复制一份再进行操作。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000005, maxt = 1005;
int N, Q, H[maxn], F[maxn];
int tot, id[maxn], L[maxt], R[maxt], add[maxt];

void rst(int i)
{
    
    
    memcpy(F + L[i], H + L[i], sizeof(int) * (R[i] - L[i] + 1));
    sort(F + L[i], F + R[i] + 1);
}

void change(int l, int r, int x)
{
    
    
    int p = id[l], q = id[r];
    if (p == q)
    {
    
    
        for (int i = l; i <= r; ++i)
            H[i] += x;
        rst(p);
    }
    else
    {
    
    
        for (int i = l; i <= R[p]; ++i)
            H[i] += x;
        for (int i = L[q]; i <= r; ++i)
            H[i] += x;
        rst(p), rst(q);
        for (int i = p + 1; i <= q - 1; ++i)
            add[i] += x;
    }
}

int ask(int l, int r, int x)
{
    
    
    int p = id[l], q = id[r], cnt = 0;
    if (p == q)
        for (int i = l; i <= r; ++i)
            cnt += H[i] + add[p] >= x;
    else
    {
    
    
        for (int i = l; i <= R[p]; ++i)
            cnt += H[i] + add[p] >= x;
        for (int i = L[q]; i <= r; ++i)
            cnt += H[i] + add[q] >= x;
        for (int i = p + 1; i <= q - 1; ++i)
            cnt += R[i] - L[i] + 1 - (lower_bound(F + L[i], F + R[i] + 1, x - add[i]) - (F + L[i]));
    }
    return cnt;
}

int main()
{
    
    
    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; ++i)
        scanf("%d", H + i);
    memcpy(F + 1, H + 1, sizeof(int) * N);
    int w = sqrt(N);
    for (int i = 1; i <= N; i += w)
    {
    
    
        L[++tot] = i, R[tot] = min(i + w - 1, N);
        sort(F + L[tot], F + R[tot] + 1);
    }
    for (int i = 1; i <= tot; ++i)
        for (int j = L[i]; j <= R[i]; ++j)
            id[j] = i;
    for (int i = 1, l, r, x; i <= Q; ++i)
    {
    
    
        char op;
        scanf(" %c%d%d%d", &op, &l, &r, &x);
        if (op == 'M')
            change(l, r, x);
        else
            printf("%d\n", ask(l, r, x));
    }
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114448907
今日推荐