BZOJ3343&&洛谷P2801 教主的魔法

分块,代码中有注释,并不难理解

代码

//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e6+50;
int n,m;
int block,cnt;
int a[M],b[M],bloc[M],add[M];
//add数组存储没个大块的整体加 
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline int get(int x,int z)
{
	int l=(x-1)*block+1,r=min(x*block,n);
	while (l<=r)
	{
		int mid=(l+r)>>1;
		if (b[mid]<z) l=mid+1;
		else r=mid-1; 
	}
	return (min(x*block,n)-l+1);
} //二分一下有多少大于的 
inline int query(int x,int y,int z)
{
	int ans=0;
	if (bloc[x]==bloc[y]) 
		{for (int i=x;i<=y;i++) 
			if (a[i]+add[bloc[i]]>=z) ans++;}//同一个块,暴力累加 
	else 
	{
		for (int i=x;i<=bloc[x]*block;i++)
			if (a[i]+add[bloc[i]]>=z) ans++;//边角余料 
		for (int i=(bloc[y]-1)*block+1;i<=y;i++)//同 
			if (a[i]+add[bloc[i]]>=z) ans++;
	}
	for (int i=bloc[x]+1;i<bloc[y];i++) ans+=get(i,z-add[i]);//完整的大块 
	return ans;
}
inline void reset(int x)
{
	int l=(x-1)*block+1,r=min(x*block,n);//因为可能出界 
	for (int i=l;i<=r;i++) b[i]=a[i];
	sort(b+l,b+r+1);
	return ;
}
inline void adda(int x,int y,int z)
{
	if (bloc[x]==bloc[y]) 
		for (int i=x;i<=y;i++) a[i]+=z;//同一个块,直接暴力累加 
	else 
	{
		for (int i=x;i<=bloc[x]*block;i++) a[i]+=z;//处理x与y所在块的边角余料 
		for (int i=(bloc[y]-1)*block;i<=y;i++) a[i]+=z;//同 
	}
	reset(bloc[x]);reset(bloc[y]);//排序 
	for (int i=bloc[x]+1;i<bloc[y];i++) add[i]+=z;//区间整体+z 
	return ;
}
int main()
{
	n=read();m=read();block=sqrt(n);
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++)
		bloc[i]=(i-1)/block+1,b[i]=a[i];
	if (n%block) cnt=n/block+1;
	else cnt=n/block;
	for (int i=1;i<=cnt;i++) reset(i);	
	for (int i=1;i<=m;i++)
	{
		char fl[3];int x,y,z;
		scanf("%s%d%d%d",fl,&x,&y,&z);
		if (fl[0]=='M') adda(x,y,z);
		else cout<<query(x,y,z)<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/acerandaker/article/details/81022150