洛谷2801教主的魔法(分块)

刚学习了分块,写篇题解庆祝一下qwq

分块的核心思想是:把一段连续的区间分成若干块,这样当你询问的区间跨过了一块,原本 O ( n ) 的复杂度会降为 O ( 1 ) 或 O ( log ) ,其他零散的部分就暴力,这样就降低了一次询问的复杂度,于是就可以舒服的切掉线段树不能做的题了qwq

分块的建立代码:

void build(){//b=belong , le=length , k=块
    le=sqrt(n);
    k=n/le;
    if(le*le<n) k++;
    for(int i=1;i<=n;i++) b[i]=(i-1)/le+1,d[i]=a[i];
    for(int i=1;i<=k;i++) l[i]=(i-1)*le+1,r[i]=i*le;
    r[k]=n;
    //接下来是一个块中需要存储的信息
}

接下来讲一讲洛谷2801这道傻逼分块题:


题目描述

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

\(Solution:\)

对于每一块,你需要的只是将元素存到另一个d数组并对其排序

对于修改,多余的部分暴力,再重新放到d数组排一次序;中间部分,对每一块开一个p数组,表示这一块的所有元素应当加上的值,因为重新修改排序后不影响元素在块中位置,所以不必全部修改

对于询问,多余的部分暴力,中间部分因为有序,所以可以二分查找

贴代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1000010,M=1010;
inline int read(){
    int f=1,w=0;char c=0;
    while(!isdigit(c))
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) w=w*10+(c^48),c=getchar();
    return f*w;
}
int n,m,k,le,l[M],r[M],a[N],b[N],d[N],p[M];
void build(){
    le=sqrt(n);
    k=n/le;
    if(le*le<n) k++;
    for(int i=1;i<=n;i++) b[i]=(i-1)/le+1,d[i]=a[i];
    for(int i=1;i<=k;i++) l[i]=(i-1)*le+1,r[i]=i*le;
    r[k]=n;
    for(int i=1;i<=k;i++) sort(d+l[i],d+r[i]+1);
}
void change(int la,int lb,int v){
    int ba=b[la],bb=b[lb];
    if(ba==bb)
    {
        for(int i=la;i<=lb;i++) a[i]+=v;
        for(int i=l[ba];i<=r[ba];i++) d[i]=a[i];
        sort(d+l[la],d+r[lb]+1);
    }
    else
    {
        for(int i=la;i<=r[ba];i++) a[i]+=v;
        for(int i=l[ba];i<=r[ba];i++) d[i]=a[i];
        sort(d+l[ba],d+r[ba]+1);
        for(int i=l[bb];i<=lb;i++) a[i]+=v;
        for(int i=l[bb];i<=r[bb];i++) d[i]=a[i];
        sort(d+l[bb],d+r[bb]+1);
        for(int i=ba+1;i<=bb-1;i++) p[i]+=v;
    }
}
int ask(int la,int lb,int c){
    int ans=0,ba=b[la],bb=b[lb];
    if(ba==bb)
    {
        for(int i=la;i<=lb;i++)
            if(a[i]+p[ba]>=c) ans++;
    }
    else
    {
        for(int i=la;i<=r[ba];i++)
            if(a[i]+p[ba]>=c) ans++;
        for(int i=l[bb];i<=lb;i++)
            if(a[i]+p[bb]>=c) ans++;
        for(int i=ba+1;i<=bb-1;i++)
        {
            int ll=l[i],rr=r[i],res=0,mid;
            while(ll<=rr)
            {
                mid=(ll+rr)>>1;
                if(d[mid]+p[i]>=c)
                    rr=mid-1,res=r[i]-mid+1;
                else ll=mid+1;
            }
            ans+=res;
        }
    }
    return ans;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build();
    while(m--)
    {
        char c;cin>>c;
        int x=read(),y=read(),z=read();
        if(c=='A') printf("%d\n",ask(x,y,z));
        else change(x,y,z);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ajy-shi-cj-zui-cai/p/10385611.html