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

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给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;
}

原文地址:https://www.cnblogs.com/hhh1109/p/8597749.html

时间: 2024-10-08 07:01:32

教主的魔法[分块+二分]的相关文章

Luogu 2801 教主的魔法 | 分块模板题

Luogu 2801 教主的魔法 | 分块模板题 我犯的错误: 有一处l打成了1,还看不出来-- 缩小块大小De完bug后忘了把块大小改回去就提交--还以为自己一定能A了-- #include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef long long ll; #define space putchar(' ')

【BZOJ-3343】教主的魔法 分块

3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 950  Solved: 414[Submit][Status][Discuss] Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内

BZOJ 3343: 教主的魔法 [分块]【学习笔记】

3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1172  Solved: 526[Submit][Status][Discuss] Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)

bzoj 3343: 教主的魔法 分块

3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MB Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W.(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L

[BZOJ3343] 教主的魔法|分块

3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 601  Solved: 259[Submit][Status][Discuss] Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内

【bzoj3343】教主的魔法 分块

[bzoj3343]教主的魔法 2014年4月26日8092 Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W.(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高) CYZ.光哥和ZJQ等

P2801 教主的魔法 (分块)

题目传送 长度为\(n(n\le 1000000)\)的数组,\(q(q\le 3000)\) 次操作.修改操作即将某个区间的值增加某个不大于1000的值,查询操作即查询某个区间比C大于等于的数有多少个 我们用一个数组\(add[i]\)来表示第\(i\)段增量,如果查询区间完全包含第\(i\)段,那么就相当于是在原数组中查找大于等于\(C-add[i]\)的数,怎么找?排序后二分找.而对于左右不完整的那部分,直接暴力查询就可以. 对于修改操作.整段的直接增加增量,不完整的直接修改原数组,然后重

BZOJ_3343_教主的魔法_分块+二分查找

题意:教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列被编号为1.2.--.N.每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W.(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)询问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效. 分析:暴力! 每一

教主的魔法(分块模板)

教主的魔法(luogu) Description 题目描述 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W.(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高) CYZ.光哥和ZJQ等人不信教主的邪,于是他们有