zoukankan      html  css  js  c++  java
  • 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
    ",query(x,y,z));
        }
        return 0;
    }
  • 相关阅读:
    HMMPfam的安装使用手记(转载)
    Linux下MySQL忘记密码的解决方法
    systemctl命令
    Linux配置ssh免密登录
    Linux下scp报Permission denied错误的解决方法
    Ubuntu修改时区和时间
    Ubuntu安装JDK
    Ubuntu用apt安装MySQL
    IntelliJ IDEA集成工具Database连接MySQL8.0报错的解决方法
    tar命令
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10587495.html
Copyright © 2011-2022 走看看