#分块+二分# [luogu P2801] 教主的魔法

Title

P2801 教主的魔法


Solution

注意lower_bound求的是大于等于,upper_bound求的是大于

之前还是比较纳闷有什么题目是分块做的了但是线段树做不了的,发现这道题目可以练一下分块
似乎洛谷上线段树的做法的都是可以被卡掉的。
但是听说: 线段树+分治似乎可以做。

可以重构(排序)每一个区间,以至于单调递增
这样的话,在询问的时候,可以二分处理。
时间复杂度 O ( q ( n log ⁡ n ) ) O(q\sqrt{(n\log n)} ) O(q(nlogn) )


Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#define rep(i,x,y) for(int i=x;i<=y;i++)
using namespace std; 
const int N=1e6+5,M=1e6+5; 
int n,m,a[N],w,pos[N],L[M],R[M],b[N],add[M]; 
int read(){
	int p=0; char c=getchar(); 
	while(!isdigit(c)) c=getchar(); 
	while(isdigit(c)) p=(p<<3)+(p<<1)+c-48,c=getchar(); 
	return p; 
}
void reset(int x){
	rep(i,L[x],R[x]) b[i]=a[i]; 
	sort(b+L[x],b+R[x]+1); 
}
void change(int l,int r,int z){
	int p=pos[l],q=pos[r]; 
	if (p==q) {
		rep(i,l,r) a[i]+=z;
	} else {
		rep(i,l,R[p]) a[i]+=z; 
		rep(i,L[q],r) a[i]+=z; 
		rep(i,p+1,q-1) add[i]+=z; reset(p),reset(q); 
	}
}
int ask_sum(int l,int r,int z){
	int val=0; 
	rep(i,l,r) if (a[i]>=z) val++; 
	return val; 
}
int find(int l,int r,int z){
	int mid,rr=r; 
	while (l<=r){
		mid=(l+r)>>1; 
		if (b[mid]<z) l=mid+1; else r=mid-1; 
	}
	return rr-l+1; 
}
// =upper_bound()> lower_bound()>=  //maybe it has wrong 
int ask(int l,int r,int z){
	int p=pos[l],q=pos[r],val=0; 
	if (p==q) return ask_sum(l,r,z-add[p]); else {
		rep(i,p+1,q-1) /*val+=R[i]-(upper_bound(b+L[i],b+R[i]+1,z)-b); */ val+=find(L[i],R[i],z-add[i]); 
		return val+ask_sum(l,R[p],z-add[p])+ask_sum(L[q],r,z-add[q]); 
	}
}
int main(){
	n=read(),m=read(); 
	rep(i,1,n) a[i]=read(); 
	w=(int)sqrt(n); 
	rep(i,1,w) L[i]=R[i-1]+1,R[i]=i*w; 
	if (R[w]<n) w++,L[w]=R[w-1]+1,R[w]=n; 
	rep(i,1,w) {rep(j,L[i],R[i]) pos[j]=i; reset(i);}
	while(m--){
		char c; 
		cin>>c; 
		int l=read(),r=read(),z=read(); 
		if (c=='M') change(l,r,z); else if (c=='A') printf("%d\n",ask(l,r,z)); 
	}
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值