洛谷 P2801 教主的魔法

题目链接

话说分块的坑点好多啊,一不小心就会越界什么的真是麻烦QWQ

0x00 思路

面对区间修改区间查询,我们有两种基本思路:线段树和分块

可是很明显我们查询的东西不满足区间加法的性质,而且每次查询的标准在变,我们考虑直接上分块暴力维护

0x01 分块思路

定义块大小为\(size = sqrt(n)\)

查询/修改的区间为\([l,r]\),查询/修改的值为\(v\)

\(l\)所在的块为\(lb\),\(r\)所在的块为\(rb\)

查询:

  1. 对于完整的块,我们将其中的元素排序,二分查找最小的符合条件的数的位置,作差即可计算贡献

  2. 对于不完整的块,我们直接枚举每一个数,计算贡献

修改:

  1. 对于完整的块,我们用\(block\)数组打标记,表示整个块全部加上\(v\),就像线段树的懒标记一样,查找的时候刚好可以用上

  2. 对于不完整的块,我们直接枚举每个数,加上\(v\),然后重新将块内的元素排序

细节:

  1. 我们开两个数组,\(a\)和\(tmp\),\(a\)数组存储的是原数组,\(tmp\)存储的是排序后的数组,排序我们是每次只排序一块内的数

  2. 每次要重排序块内的数时,可以从\(a\)中复制一份到\(tmp\)中,然后排序

  3. 各种下标要注意,时刻保证块的右端点不能超过\(n\)

  4. 对于\(lb = rb\)的情况,要特判

0x02 Code


#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define M 1010
int read(){
    int x=0; char c=getchar(); int flag=1;
    while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
    while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
    return x*flag;
}
int n,m,size,belong[N];
long long a[N],tmp[N],block[M];
signed main(){
    n = read(),m = read(),size = sqrt(n);
    for(int i = 1;i <= n;i ++) a[i] = read(),belong[i] = (i - 1) / size + 1,tmp[i] = a[i];
    for(int i = 1;(i - 1) * size + 1 <= n;i ++){
        int l = (i - 1) * size + 1,r = min(n,i * size);
        sort(tmp + l,tmp + r + 1);
    }
    while(m --){
        char opt;
        int l,r,v;
        scanf(" %c %d %d %d",&opt,&l,&r,&v);
        if(opt == 'A'){
            int lb = belong[l],rb = belong[r],ans = 0;
            if(lb == rb){
                for(int i = l;i <= r;i ++) if(a[i] + block[lb] >= v) ++ ans;
                printf("%d\n",ans);
                continue;
            }
            for(int i = lb + 1;i <= rb - 1;i ++){
                int l = (i - 1) * size + 1,r = i * size,ps = r + 1;
                while(l <= r){
                    int mid = ((l + r) >> 1);
                    if(tmp[mid] + block[i] >= v) { ps = mid; r = mid - 1; }
                    else l = mid + 1;
                }
                ans += i * size - ps + 1;
            }
            for(int i = l;i <= lb * size;i ++) if(a[i] + block[lb] >= v) ++ ans;
            for(int i = (rb - 1) * size + 1;i <= r;i ++) if(a[i] + block[rb] >= v) ++ ans;
            printf("%d\n",ans); 
        }
        if(opt == 'M'){
            int lb = belong[l],rb = belong[r];
            if(lb == rb){
                for(int i = l;i <= r;i ++) a[i] += v;
                for(int i = (lb - 1) * size + 1;i <= min(lb * size,n);i ++) tmp[i] = a[i];
                sort(tmp + (lb - 1) * size + 1,tmp + min(lb * size,n) + 1);
                continue;
            }
            for (int i = lb + 1;i <= rb - 1;i ++) block[i] += v;
            
            for (int i = l;i <= lb * size;i ++) a[i] += v;
            for (int i = (lb - 1) * size + 1;i <= lb * size;i ++) tmp[i] = a[i];
            sort(tmp + (lb - 1) * size + 1,tmp + lb * size + 1);
            
            for (int i = (rb - 1) * size + 1;i <= r;i ++) a[i] += v;
            for (int i = (rb - 1) * size + 1;i <= min(rb * size,n);i ++) tmp[i] = a[i];
            sort(tmp + (rb - 1) * size + 1,tmp + min(rb * size,n) + 1);
        }
    }
    return 0;
}

上一篇:初级模拟电路:3-9 BJT三极管实现逻辑门


下一篇:brew安装指定版本的软件