洛谷 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;
}


posted @ 2020-01-29 12:41  zhuzihan  阅读(90)  评论(0编辑  收藏  举报