1. 程式人生 > >luogu2801教主的魔法(分塊+二分)

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