首页 > 代码库 > BZOJ 3343 教主的魔法 分块

BZOJ 3343 教主的魔法 分块

题目大意:给定一个序列,提供两种操作:

1.区间加上一个数

2.询问区间中有多少大于等于C的数

n<=100W,树套树不用想了,Q<=3000,分块走起~

将原数组复制一份副本,副本中每一块排序

对于每次修改,中间块的部分打标记,两边修改后重建

对于每次查询,中间块的部分二分答案,两边暴力枚举

别忘考虑标记

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1001001
using namespace std;
int n,m,ans,block,a[M],b[M],mark[1010];
void Rebuild(int x)
{
	memcpy(b+x*block+1,a+x*block+1,sizeof(a[0])*(x*block+block<=n?block:n-x*block) );
	sort(b+x*block+1,b+min(x*block+block,n)+1);
}
int main()
{
	int i,j,x,y,z;
	char p[10];
	cin>>n>>m;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]),b[i]=a[i];
	block=static_cast<int>(sqrt(n)+1e-7);
	for(i=0;i*block+1<=n;i++)
		sort(b+i*block+1,b+min(i*block+block,n)+1);
	for(i=1;i<=m;i++)
	{
		scanf("%s%d%d%d",p,&x,&y,&z);
		if(p[0]=='M')
		{
			if((x-1)/block==(y-1)/block)
			{
				for(j=x;j<=y;j++)
					a[j]+=z;
				Rebuild((x-1)/block);
				continue;
			}
			int b1=(x-2)/block+1;
			int b2=y/block-1;
			for(j=b1;j<=b2;j++)
				mark[j]+=z;
			for(j=x;j<=b1*block;j++)
				a[j]+=z;
			for(j=b2*block+block+1;j<=y;j++)
				a[j]+=z;
			Rebuild((x-1)/block);
			Rebuild((y-1)/block);
		}
		else
		{
			ans=0;
			if((x-1)/block==(y-1)/block)
			{
				for(j=x;j<=y;j++)
					if(a[j]+mark[(j-1)/block]>=z)
						++ans;
				printf("%d\n",ans);
				continue;
			}
			int b1=(x-2)/block+1;
			int b2=y/block-1;
			for(j=b1;j<=b2;j++)
				ans+=(b+min(j*block+block,n)+1)-lower_bound(b+j*block+1,b+min(j*block+block,n)+1,z-mark[j]);
			for(j=x;j<=b1*block;j++)
				if(a[j]+mark[(j-1)/block]>=z)
					++ans;
			for(j=b2*block+block+1;j<=y;j++)
				if(a[j]+mark[(j-1)/block]>=z)
					++ans;
			printf("%d\n",ans);
		}
	}
	
}


BZOJ 3343 教主的魔法 分块