教主的魔法 ---分块 + 二分

传送门

洛谷P2801

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。

每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)

CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

WD巨懒,于是他把这个回答的任务交给了你。

分析

 简要分析本题:给定一个区间,有两种操作:
  1.区间每个元素均加上一个值
  2.询问区间内>=k的个数

 对于区间的操作,首先会想到线段树,但对于第二个操作,普通的线段树就有点无力了。对此,换个思路,考虑暴力的分块。

 对于操作1:完整块直接标记(那么查询时c - add);部分块直接修改
 对于操作2:完整块间考虑二分(对此,要另设数组,记录排序后的数组);部分块暴力查询

 注意事项:
  1.区间的分类处理
  2.末区间本身就不完整
  3.下面的程序中,a数组仅记录下标;同时,块部分修改后,整个块重新排序
  4.为了方便,下标从0开始

代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>

#define IL inline
#define open(s) freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);
#define close fclose(stdin); fclose(stdout);

using namespace std;

IL int read()
{
    int sum = 0, k = 1;
    char c = getchar();
    for(;'0' > c || c > '9'; c = getchar())
    if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar())
        sum = sum * 10 + c - '0';
    return sum * k;
}

int n, m;
int block;
int num[1000005], a[1000005];

struct Block
{
    int val;
    int l, r;
} blo[1005];

IL bool cmp(int x, int y)
{
    return num[x] < num[y];
}

IL void update(int x, int y, int z)
{
    for(int i = x; i <= y; ++i)
        num[i] += z;
    int k = x / block;
    sort(a + blo[k].l, a + blo[k].r + 1, cmp);
}

IL int query1(int x, int y, int z)
{
    int sum = 0;
    for(int i = x; i <= y; ++i)
    if(num[i] >= z) ++sum;
    return sum;
}

IL int query2(int x, int y, int z)
{
    int l = x, r = y, p = l - 1;
    for(int mid; l <= r;)
    {
        mid = (l + r) >> 1;
        if(num[a[mid]] < z)
        {
            p = mid;
            l = mid + 1;
        }else
        {
            r = mid - 1;
        }
    }
    return y - p;
}

int main()
{
    open("2801")

    n = read(); m = read(); block = sqrt(n);
    for(int i = 0, pre = 0, k = 0; i < n; ++i)
    {
        num[i] = read(); a[i] = i;
        if(!((i + 1) % block) || i + 1 == n)
        {
            blo[k].r = i;
            sort(a + blo[k].l, a + i + 1, cmp);
            blo[++k].l = i + 1;
        }
    }
    char c;
    for(int i = 1, x, y, z, l, r, ans; i <= m; ++i)
    {
        scanf(" %c", &c);
        x = read() - 1; y = read() - 1; z = read();
        l = x / block; r = y / block;
        if(c == 'M')
        {
            if(l == r) update(x, y, z); else
            {
                if(x != blo[l].l) {update(x, blo[l].r, z); ++l;}
                if(y != blo[r].r) {update(blo[r].l, y, z); --r;}
                for(; l <= r; ++l) blo[l].val += z;
            }
        }else
        if(c == 'A')
        {
            if(l == r) ans = query1(x, y, z - blo[l].val); else
            {
                ans = 0;
                if(x != blo[l].l) {query1(x, blo[l].r, z - blo[l].val); ++l;}
                if(y != blo[r].r) {query1(blo[r].l, y, z - blo[r].val); --r;}
                for(; l <= r; ++l) ans += query2(blo[l].l, blo[l].r, z - blo[l].val);
            }
            printf("%d\n", ans);
        }
    }

    close
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/81055953