P2801 教主的魔法

时间:2019-03-24
本文章向大家介绍P2801 教主的魔法,主要包括P2801 教主的魔法使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

传送门

$N$ 太大了主席树过不了

考虑分块

对每个块内的元素排序,询问就对大块二分查找,对两边小的部分暴力枚举

修改时维护 $add[i]$ 标记,维护当前块内整块已经加的数

那么整块的就直接修改 $add$ ,两边小的部分就把那两个的块暴力修改然后重新排序

然后注意一下边界就完了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e6+7,M=2e3+7,INF=1e9+7;
int n,m;
int a[N];
int bel[N],L[M];//bel[i]存位置i所在的块,L[i]存第i个块的左端点
int blk[M][M],tmp[M],add[M];//blk就是分出来的块排序后的东西
inline void change(int l,int r,int k)//修改
{
    if(bel[l]==bel[r])//要特判只有小块的情况
    {
        int tot=0,be=bel[l];
        for(int i=L[be];i<l;i++) tmp[++tot]=a[i];//把数直接扔到tmp里
        for(int i=l;i<=r;i++) a[i]+=k,tmp[++tot]=a[i];//有修改要修改a[i]
        for(int i=r+1;i<L[be+1];i++) tmp[++tot]=a[i];
        sort(tmp+1,tmp+tot+1);
        memcpy(blk[be],tmp,sizeof(tmp));//排序后扔给blk
    }
    int pl=bel[l-1]+1,pr=bel[r+1]-1,tot=0;//找到整块
    for(int i=pl;i<=pr;i++) add[i]+=k;
    if(pl>bel[1])//处理左边小块
    {
        for(int i=L[pl-1];i<l;i++) tmp[++tot]=a[i];
        for(int i=l;i<L[pl];i++) a[i]+=k,tmp[++tot]=a[i];
        sort(tmp+1,tmp+tot+1);
        memcpy(blk[pl-1],tmp,sizeof(tmp));
    }
    if(pr<bel[n])//右边
    {
        for(int i=L[pr+1];i<=r;i++) a[i]+=k,tmp[++tot]=a[i];
        for(int i=r+1;i<L[pr+2];i++) tmp[++tot]=a[i];
        sort(tmp+1,tmp+tot+1);
        memcpy(blk[pr+1],tmp,sizeof(tmp));
    }
}
inline int query(int l,int r,int k)//处理询问
{
    if(bel[l]==bel[r])//特判没有整块
    {
        int res=0;
        for(int i=l;i<=r;i++) if(a[i]>=k-add[bel[i]]) res++;
        return res;
    }
    int pl=bel[l-1]+1,pr=bel[r+1]-1,res=0;
    for(int i=pl;i<=pr;i++)//对大块的二分
        res+=(L[i+1]-L[i]) -( lower_bound(blk[i]+1 , blk[i]+(L[i+1]-L[i])+1 , k-add[i]) -blk[i])+1;
    for(int i=l;i<L[pl];i++) if(a[i]>=k-add[bel[i]]) res++;//剩下的暴力计算
    for(int i=L[pr+1];i<=r;i++) if(a[i]>=k-add[bel[i]]) res++;
    return res;
}
int main()
{
    n=read(),m=read(); int t=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        bel[i]=(i-1)/t+1;
        if(bel[i]!=bel[i-1]) L[bel[i]]=i;
        else L[bel[i]]=L[bel[i-1]];
        a[i]=read();
    }
    bel[n+1]=bel[n]+1; L[bel[n+1]]=n+1;//注意可能会访问到n+1
    for(int i=1;i<=bel[n];i++)//预处理blk
    {
        int tot=0;
        for(int j=L[i];j<L[i+1];j++) tmp[++tot]=a[j];
        sort(tmp+1,tmp+tot+1);
        memcpy(blk[i],tmp,sizeof(tmp));
    }
    char s[7]; int x,y,z;
    while(m--)
    {
        scanf("%s",s); x=read(),y=read(),z=read();
        if(s[0]=='M') change(x,y,z);
        else printf("%d\n",query(x,y,z));
    }
    return 0;
}