洛谷 P2801 教主的魔法 解题报告

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行为两个整数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的英雄数。

数据范围

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

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


考虑使用分块维护

发现没有添加或者删除,我们可以不写分裂合并

我们可以维护\(D\)个块,每个块大小为\(S\)

我们保证每个块都是有序的,这样

在查询的时候,边角直接暴力,整块二分即可

在修改的时候,边角直接暴力,整块打标记

发现单次操作的复杂度大概是\(O(DlogS+S)\)

我们可以把块放的稍稍比\(\sqrt N\)大一点之类的


Code:

#include <cstdio>
#include <algorithm>
#include <cmath>
const int S=1e3+10;
const int N=1e6+10;
struct node
{
    int pos,dat;
    bool friend operator <(node n1,node n2)
    {
        return n1.dat<n2.dat;
    }
}t[N];
int belong[N],L[S],R[S],lazy[S],delta[S],n,m,si,s;
void init()
{
    scanf("%d%d",&n,&m);
    s=sqrt(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&t[i].dat);
    for(si=1;si*s<=n;si++)
    {
        L[si]=(si-1)*s+1,R[si]=si*s;
        for(int j=L[si];j<=R[si];j++)
            belong[j]=si,t[j].pos=j;
    }
    if(R[si]<n)
    {
        L[si]=R[si-1]+1,R[si]=n;
        for(int j=L[si];j<=R[si];j++)
            belong[j]=si,t[j].pos=j;
    }
    L[si+1]=R[si]+1;
    for(int i=1;i<=si;i++)
        std::sort(t+L[i],t+R[i]+1);
}
void change(int l,int r,int w)
{
    if(belong[l]==belong[r])
    {
        int now=belong[l];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos<=r&&t[i].pos>=l)
                t[i].dat+=w;
        std::sort(t+L[now],t+R[now]+1);
    }
    else
    {
        for(int i=belong[l]+1;i<belong[r];i++)
            delta[i]+=w;
        int now=belong[l];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos>=l)
                t[i].dat+=w;
        std::sort(t+L[now],t+R[now]+1);
        now=belong[r];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos<=r)
                t[i].dat+=w;
        std::sort(t+L[now],t+R[now]+1);
    }
}
int query(int l,int r,int w)
{
    int sum=0;
    if(belong[l]==belong[r])
    {
        int now=belong[l];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos>=l&&t[i].pos<=r&&t[i].dat+delta[now]>=w)
                ++sum;
    }
    else
    {
        for(int i=belong[l]+1;i<belong[r];i++)
        {
            int ll=L[i]-1,rr=R[i],tmp=t[ll].dat;
            t[ll].dat=0;
            while(ll<rr)
            {
                int mid=ll+rr+1>>1;
                if(t[mid].dat+delta[i]<w)
                    ll=mid;
                else
                    rr=mid-1;
            }
            sum+=R[i]-ll;t[L[i]-1].dat=tmp;
        }
        int now=belong[l];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos>=l&&t[i].dat+delta[now]>=w)
                ++sum;
        now=belong[r];
        for(int i=L[now];i<=R[now];i++)
            if(t[i].pos<=r&&t[i].dat+delta[now]>=w)
                ++sum;
    }
    return sum;
}
void work()
{
    char c[3];
    for(int l,r,w,i=1;i<=m;i++)
    {
        scanf("\n%c%d%d%d",c,&l,&r,&w);
        if(c[0]=='M') change(l,r,w);
        else printf("%d\n",query(l,r,w));
    }
}
int main()
{
    init();
    work();
    return 0;
}


2018.8.25

posted @ 2018-08-25 11:42  露迭月  阅读(173)  评论(0编辑  收藏  举报