教主的魔法 【分块】

教主的魔法

分块模板题
每一个M操作就是区间修改+暴力修改,每一个A操作就是区间查询,(A正解能用主席树,但是分析数据范围之后发现分块就够了)。
分析之后发现M操作就是分块基本操作,直接按照整区间内的数直接整块修改,不是整区间内的数暴力修改(也就是分块最重要的思想)来操作即可。
但是看到A操作发现直接查询时间复杂度会炸掉,于是考虑如何优化。发现如果区间内每个数都是有序的那么查询直接二分即可,则想到再引入一个数组b进行有序化原数组的操作,对于每一次修改,都对b进行一次更新加重新排序操作,然后直接在b数组上查询即可。

#include <bits/stdc++.h>
using namespace std;
const int N=1000000+50;
int a[N],b[N],bas,n,t,st[N],ed[N],pos[N],add[N],q,L,R,X;
int ans;
char s[N];
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c^48);c=getchar();}
	return cnt*f;
}
inline void prework(){
	for(int i=1;i<=t;++i){st[i]=(i-1)*bas+1;ed[i]=i*bas;}ed[t]=n;
	for(int i=1;i<=n;++i){pos[i]=(i-1)/bas+1;}
	for(int i=1;i<=t;++i){
		sort(b+st[i],b+ed[i]+1);
	}
}
inline void change(int L,int R,int X){
	int p=pos[L],q=pos[R];
	if(p==q){
		for(int j=L;j<=R;++j){a[j]+=X;}
		for(int j=st[p];j<=ed[q];++j){b[j]=a[j];}
		sort(b+st[p],b+ed[p]+1);
	}
	else{
		//第一个整块之前
		for(int j=L;j<=ed[p];++j){a[j]+=X;}
		for(int j=st[p];j<=ed[p];++j){b[j]=a[j];}
		sort(b+st[p],b+ed[p]+1);
		//最后一个整块之后
		for(int j=st[q];j<=R;++j){a[j]+=X;}
		for(int j=st[q];j<=ed[q];++j){b[j]=a[j];}
		sort(b+st[q],b+ed[q]+1);
		//整块
		 for(int j=p+1;j<=q-1;++j){add[j]+=X;}
	}
}
inline void query(int L,int R,int X){
	ans=0;
	int p=pos[L],q=pos[R];
	if(p==q){
		for(int j=L;j<=R;++j){
			if(add[p]+a[j]>=X) ++ans;
		}
		printf("%d\n",ans);
		return;
	}
	else{
		for(int j=L;j<=ed[p];++j){if(add[p]+a[j]>=X) ++ans;}
		for(int j=st[q];j<=R;++j){if(add[q]+a[j]>=X) ++ans;}
		for(int j=p+1;j<=q-1;++j){
			int ll=st[j],rr=ed[j],mid,res=0;
			while(ll<=rr){
				mid=(ll+rr)>>1;
				if(b[mid]+add[j]>=X){
					rr=mid-1,res=ed[j]-mid+1;
				}
				else ll=mid+1;
			}
			ans+=res;
		}
		printf("%d\n",ans);
		return;
	}
}
signed main(){
	n=read();q=read();bas=sqrt(n);t=n/bas;if(n%bas) ++t;
	for(int i=1;i<=n;++i){a[i]=b[i]=read();}
	prework();
	for(int i=1;i<=q;++i){
		scanf("%s",s);
		L=read(),R=read(),X=read();
		if(s[0]=='M') change(L,R,X);
		if(s[0]=='A') query(L,R,X);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值