【BZOJ】3343 教主的魔法 分块

110 篇文章 0 订阅
1 篇文章 0 订阅

题目传送门

分块什么的最好了~(至少简单易懂,代码还比较好写)

这题就当是我学习了分块的flag吧。

其实分块就是把原来n个元素分成sqrt(n)块,每块有sqrt(n)个元素。

对于一段连续的区间修改,我们可以把这个区间分成若干个完整的块和两端剩下的元素。

对于完整的块,我们可以用和线段树一样的思想——延迟标记来记录当前块内所有元素的共同修改量;

对于两端剩下的元素,我们可以暴力修改。(暴力大法好~)

对于询问,我们可以再开一个数组,和之前的数组一样分块,但是要把每个块内的元素排序。

然后我们把询问拆分成若干个完整的块和两端剩下的元素。

对于完整的块,我们可以二分出块内的答案;对于两端剩下的元素,依然是暴力……

综上,这道题就被分块水过去了。(因为询问的次数比较小嘛)

附上AC代码:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
#define N 1010
using namespace std;

int n,m,size,num,a[N*N],l[N],r[N],b[N*N],x,y,w,sy[N*N],lz[N];
char c;

inline char nc(){
	static char ch[100010],*p1=ch,*p2=ch;
	return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &a){
	static char c=nc();int f=1;
	for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
	for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
	a*=f;return;
}

inline void rebuild(int x){
	for (int i=l[x]; i<=r[x]; ++i) b[i]=a[i];
	return sort(b+l[x],b+r[x]+1);
}

inline void change(int x,int y,int w){
	if (sy[x]==sy[y]){
		for (int i=x; i<=y; ++i) a[i]+=w;
		return rebuild(sy[x]);
	}
	for (int i=sy[x]+1; i<=sy[y]-1; ++i) lz[i]+=w;
	for (int i=x; i<=r[sy[x]]; ++i) a[i]+=w;rebuild(sy[x]);
	for (int i=l[sy[y]]; i<=y; ++i) a[i]+=w;rebuild(sy[y]);
	return;
}

inline int find(int x,int y,int w){
	int l=x,r=y,ans=y+1,mid;
	while (l<=r){
		mid=(l+r)>>1;
		if (b[mid]<w) l=mid+1;
			else ans=mid,r=mid-1;
	}
	return y-ans+1;
}

inline int query(int x,int y,int w){
	int ans=0;
	if (sy[x]==sy[y]){
		for (int i=x; i<=y; ++i) if (a[i]>=w) ++ans;
		return ans;
	}
	for (int i=sy[x]+1; i<=sy[y]-1; ++i) ans+=find(l[i],r[i],w-lz[i]);
	for (int i=x; i<=r[sy[x]]; ++i) if (a[i]>=w) ++ans;
	for (int i=l[sy[y]]; i<=y; ++i) if (a[i]>=w) ++ans;
	return ans;
}

int main(void){
	read(num),read(m),size=sqrt(num),n=(num-1)/size+1;
 	for (int i=1; i<=num; ++i){
		read(a[i]),b[i]=a[i],sy[i]=(i-1)/size+1;
		if (i%size==1) l[sy[i]]=i;
		if (i%size==0) r[sy[i]]=i;
	}
	if (!r[n]) r[n]=num;
	for (int i=1; i<=n; ++i) sort(b+l[i],b+r[i]+1);
	while (m--){
		c=nc();while (c!='M'&&c!='A') c=nc();
		read(x),read(y),read(w);
		switch (c){
			case 'M': change(x,y,w); break;
			case 'A': printf("%d\n",query(x,y,w)); break;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值