教主的魔法[分块+二分]

Posted AFO过气选手

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了教主的魔法[分块+二分]相关的知识,希望对你有一定的参考价值。

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给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行为两个整数N、Q。Q为问题数与教主的施法数总和。

第2行有N个正整数,第i个数代表第i个英雄的身高。

第3到第Q+2行每行有一个操作:

(1) 若第一个字母为“M”,则紧接着有三个数字L、R、W。表示对闭区间 [L, R] 内所有英雄的身高加上W。

(2) 若第一个字母为“A”,则紧接着有三个数字L、R、C。询问闭区间 [L, R] 内有多少英雄的身高大于等于C。

输出格式:

对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。

输入输出样例

输入样例#1: 复制
5 3

1 2 3 4 5

A 1 5 4

M 3 5 1

A 1 5 4
输出样例#1: 复制
2
3

说明

【输入输出样例说明】

原先5个英雄身高为1、2、3、4、5,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为1、2、4、5、6,此时[1, 5]间有3个英雄的身高大于等于4。

【数据范围】

对30%的数据,N≤1000,Q≤1000。

对100%的数据,N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000

 

 

 

 

 

题解

  分块+排序+二分

  首先我们把这个题目拆开来看:

  (1)要询问或给(l,r)加一个值

  (2)在(l,r)区间的值是不定值,还要求的是大于等于k的数有多少

  由(1)–>得尝试分块和分块的加法标记

  由(2)–>得我们可以事先处理好每一块的顺序,然后找到大于等于k的第一个数,就能求出每一块的贡献值了–>sort+vector

  要注意的是,当l,r处在两个不完整的块,暴力加上在排序就ok了

  ps:我用了stl,洛谷要开o2,或者考虑手打二分

 

 

 

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,tmp;
int ch[1000005],bl[1000005],addx[1000005],l[1000005],r[1000005];
vector<int>ve[5005];
int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch>9||ch<0){if(ch==-)w=-1;ch=getchar();}
    while(ch>=0&&ch<=9)x=x*10+ch-0,ch=getchar();
    return x*w;
}

void build()
{
    for(int i=1;i<=tmp;i++)
    {
        l[i]=(i-1)*tmp+1;r[i]=tmp*i;
    }
    r[tmp]=n;
    for(int i=1;i<=n;i++)
    {
        bl[i]=(i-1)/(tmp)+1;
        ve[bl[i]].push_back(ch[i]);
    }
    for(int i=1;i<=bl[n];i++)
        sort(ve[i].begin(),ve[i].end());
/*    for(int i=1;i<=bl[n];i++)
        for(int  j=0;j<ve[i].size();j++)
        cout<<ve[i][j]<<‘ ‘;*/
}

void change(int x)
{
    ve[x].clear();
    for(int i=l[x];i<=r[x];i++)
        ve[x].push_back(ch[i]);
    sort(ve[x].begin(),ve[x].end()+1);
}

void add(int x,int y,int k)
{
    if(bl[x]==bl[y])    for(int i=x;i<=y;i++)ch[i]+=k;
    else 
    {
        for(int i=x;i<=r[bl[x]];i++)ch[i]+=k;
        for(int i=l[bl[y]];i<=y;i++)ch[i]+=k;
    }
    change(bl[x]);change(bl[y]);
    for(int i=bl[x]+1;i<=bl[y]-1;i++)
    {    
        addx[i]+=k;
    }
}

int query(int x,int y,int k)
{
    int ans=0;
//    cout<<x<<‘ ‘<<y<<‘ ‘<<k<<endl;
    if(bl[x]==bl[y])for(int i=x;i<=y;i++){if(ch[i]+addx[bl[x]]>=k)ans++;}
    else 
    {
        for(int i=x;i<=r[bl[x]];i++){if(ch[i]+addx[bl[x]]>=k)ans++;}
        for(int i=l[bl[y]];i<=y;i++){if(ch[i]+addx[bl[y]]>=k)ans++;}
    }
    //    cout<<ans<<endl;
    for(int i=bl[x]+1;i<=bl[y]-1;i++)
    {
//    cout<<".."<<endl;
        int xx=k-addx[i];
        int sum=lower_bound(ve[i].begin(),ve[i].end(),xx)-ve[i].begin();
        //cout<<sum<<endl;
        ans+=tmp-sum;
//    cout<<ans<<endl;
    }
    return ans;
}

int main()
{
    n=read();m=read();tmp=sqrt(n);if(tmp*tmp<n)tmp++;
    for(int i=1;i<=n;i++)
    {
        ch[i]=read();
    }
    build();
    for(int i=1;i<=m;i++)
    {
        char f;int x,y,z;
        cin>>f;x=read();y=read();z=read();
        if(f==M)add(x,y,z);
        else printf("%d\n",query(x,y,z));
    }
    return 0;
}

 

 

 

以上是关于教主的魔法[分块+二分]的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ-3343教主的魔法+分块(大块排序二分)

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

BZOJ 3343 教主的魔法 分块

P2801 教主的魔法 (分块)

bzoj3343 教主的魔法分块入门By cellur925

[Luogu] 教主的魔法