[Noip模拟题]教主的魔法

题目

Description

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一
次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。每个人的身高一开始都是不超过1000的正整数
。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合
区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)CYZ、光哥和ZJQ等人不信教主的邪,于是他
们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。WD巨懒,于是他
把这个回答的任务交给了你。

Input

 第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。
N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000

Output

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

Sample Input

5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4

Sample Output

2
3
【输入输出样例说明】
原先5个英雄身高为1、2、3、4、5,此时[1, 5]间有2个英雄的身高大于等于4。
教主施法后变为1、2、4、5、6,此时[1, 5]间有3个英雄的身高大于等于4。


思路

这是一道分块题,依然是爆模小边块,但是整个分块要用二分,不然会超时;

那么二分就需要每一个分块都是排好序的,这样就要开一个数组,存每个分块内排序后的;

每次改变英雄身高的时候需要,重新建立一次每个分块内排序的数组;

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n,m,blo;
ll a[1000001],v[1000001],sv[1000001],b[1000001];
inline ll L(ll x)//懒人专用函数
{
    return (x-1)*blo+1;
}
inline ll R(ll x)
{
    return x*blo;
}
inline ll dowork(ll x)
{
    for(ll i=L(a[x]);i<=R(a[x]);i++)
        sv[i]=v[i];//重新构建数组
    sort(sv+L(a[x]),sv+R(a[x])+1);//排序
}
inline void findout(ll x,ll y,ll z)
{
    if(a[x]==a[y])//如果在一个分块,那么暴模
    {
        for(ll i=x;i<=y;i++)
            v[i]+=z;
        dowork(x);//排序
    }
    else
    {
        for(ll i=a[x]+1;i<=a[y]-1;i++)
            b[i]+=z;//大分块要加的,再开一个数组存
        for(ll i=x;i<=R(a[x]);i++)
            v[i]+=z;
        dowork(x);//重构
        for(ll i=L(a[y]);i<=y;i++)
            v[i]+=z;
        dowork(y);//重构
    }
}
inline ll reallyans(ll x,ll y,ll z)
{
    ll sum=0;
    if(a[x]==a[y])
    {
        for(ll i=x;i<=y;i++)
        if(v[i]+b[a[i]]>=z)
            sum++;//暴模,没问题吧
        return sum;
    }
    else
    {
        for(ll i=a[x]+1;i<=a[y]-1;i++)
        {
            ll l=L(i),r=R(i),ss=0,mid;
            while(l<=r)//二分查询
            {
                mid=(l+r)>>1;
                if(sv[mid]+b[i]>=z)//如果中间的数字都大于z
                    r=mid-1,           //往左边找是否有满足的
                    ss=R(i)-mid+1;//因为排了序,所以右边的都满足
                else
                    l=mid+1;
            }
            sum+=ss;
        }
        for(ll i=x;i<=R(a[x]);i++)
        if(v[i]+b[a[i]]>=z)
            sum++;//暴力
        for(ll i=L(a[y]);i<=y;i++)
        if(v[i]+b[a[i]]>=z)
            sum++;//还是暴力
        return sum;
    }
}
int main()
{
    n=read();m=read();blo=sqrt(n);
    for(ll i=1;i<=n;i++)
    {
        v[i]=read();
        a[i]=(i-1)/blo+1;//a数组存每个数在哪个分块
        sv[i]=v[i];//建立一个新数组存每个分块排序的
    }
    ll t=n/blo;
    if(n%blo)
        t++;//统计分块个数
    for(ll i=1;i<=t;i++)
        sort(sv+L(i),sv+R(i)+1);//排序
    char cc[4];
    ll ans;
    for(ll i=1;i<=m;i++)
    {
        scanf("%s",cc);
        ll x=read(),y=read(),z=read();
        if(cc[0]=='M')
            findout(x,y,z);
        else if(cc[0]=='A')
        {
            ans=reallyans(x,y,z);
            printf("%lld
",ans);
        }
    }
    return 0;
}

有不足的请联系^_^

 
原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/12902684.html