luogu2801教主的魔法(分块+二分)

洛谷P2801

题目
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。

每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)

CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

WD巨懒,于是他把这个回答的任务交给了你。

分析
增加身高——打标记
快速查找——排序(快排),二分

注意
分块:
——块数,平方根复杂化
——边界,min(k*block,n)
二分:
边界,最后的取值

#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
int n,m,block,num;
int a[maxn],b[maxn],ls[maxn],rs[maxn],pos[maxn],s[maxn];

void reset(int k){
	int l=(k-1)*block+1,r=min(k*block,n);
	for(int i=l;i<=r;i++) b[i]=a[i];
	sort(b+l,b+r+1);
	ls[k]=l;rs[k]=r;
	return ;
}

void work(int x,int y,int z){
	int ans=0;
	if(pos[x]==pos[y]){
		for(int i=x;i<=y;i++)
			if(a[i]+s[pos[x]]>=z)ans++;
	}
	else{
		for(int i=x;i<=rs[pos[x]];i++)if(a[i]+s[pos[x]]>=z)ans++;
		for(int i=ls[pos[y]];i<=y;i++)if(a[i]+s[pos[x]]>=z)ans++;
		for(int i=pos[x]+1;i<=pos[y]-1;i++){
			int left=ls[i],right=rs[i],mid;
			while(left<=right){
				mid=(left+right)/2;
				if(b[mid]<z-s[i])left=mid+1;
				else right=mid-1;
			}
			ans=ans+rs[i]-left+1;
		}
	}
	printf("%d\n",ans);
	return ;
}

void work1(int x,int y,int z){
	if(pos[x]==pos[y])
		for(int i=x;i<=y;i++)a[i]=a[i]+z;
	else{
		for(int i=x;i<=rs[pos[x]];i++)a[i]=a[i]+z;
		for(int i=ls[pos[y]];i<=y;i++)a[i]=a[i]+z;
	}
	reset(pos[x]);reset(pos[y]);
	for(int i=pos[x]+1;i<=pos[y]-1;i++)s[i]=s[i]+z;
	return ;
}

int main(){
	memset(s,0,sizeof(s));
	scanf("%d %d\n",&n,&m);
	block=sqrt(n*1.0);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		pos[i]=(i-1)/block+1;
		b[i]=a[i];
	}
	num=n/block;if(n%num!=0)num++;
	for(int i=1;i<=num;i++)reset(i);
	for(int i=1;i<=m;i++){
		int x,y,z;
		char h;
		scanf("\n%c %d %d %d",&h,&x,&y,&z);
		if(h=='A')work(x,y,z);
		if(h=='M')work1(x,y,z);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43342048/article/details/82947637