二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

题解(教主的魔法)P2801

题目

教主的魔法

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给 XMYZ 信息组每个英雄看。于是 \(N\) 个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为 \(1, 2, \ldots, N\)

每个人的身高一开始都是不超过 \(1000\) 的正整数。教主的魔法每次可以把闭区间 \([L, R]\)\(1≤L≤R≤N\))内的英雄的身高全部加上一个整数 \(W\)。(虽然 \(L=R\) 时并不符合区间的书写规范,但我们可以认为是单独增加第 \(L(R)\) 个英雄的身高)

CYZ、光哥和 ZJQ 等人不信教主的邪,于是他们有时候会问 WD 闭区间 \([L, R]\) 内有多少英雄身高大于等于 \(C\),以验证教主的魔法是否真的有效。

WD 巨懒,于是他把这个回答的任务交给了你。

输入格式

\(1\) 行为两个整数 \(N, Q\)\(Q\) 为问题数与教主的施法数总和。

\(2\) 行有 \(N\) 个正整数,第 \(i\) 个数代表第 \(i\) 个英雄的身高。

\(3\) 到第 \(Q+2\) 行每行有一个操作:

  1. 若第一个字母为 M,则紧接着有三个数字 \(L, R, W\)。表示对闭区间 \([L, R]\) 内所有英雄的身高加上 \(W\)

  2. 若第一个字母为 A,则紧接着有三个数字 \(L, R, C\)。询问闭区间 \([L, R]\) 内有多少英雄的身高大于等于 \(C\)

输出格式

对每个 A 询问输出一行,仅含一个整数,表示闭区间 \([L, R]\) 内身高大于等于 \(C\) 的英雄数。

样例 #1

样例输入 #1

5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4

样例输出 #1

2
3

提示

【输入输出样例说明】

原先 \(5\) 个英雄身高为 \(1, 2, 3, 4, 5\),此时 \([1, 5]\) 间有 \(2\) 个英雄的身高大于等于 \(4\)。教主施法后变为 \(1, 2, 4, 5, 6\),此时 \([1, 5]\) 间有 \(3\) 个英雄的身高大于等于 \(4\)

【数据范围】

对于 \(30\%\) 的数据,\(N≤1000\)\(Q≤1000\)

对于 \(100\%\) 的数据,\(N≤10^6\)\(Q≤3000\)\(1≤W≤1000\)\(1≤C≤10^9\)


\(\text{upd 2022.8.18}\):新增加一组 Hack 数据。

打个暴力先

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
inline int read(){
	int f(1),x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f*x;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int n,m,a[N],t,L[N],R[N];
main(void){
	n=read(),m=read();
	for(register int i=1;i<=n;i++){
		a[i]=read();
	}
	
	while(m--){
		char ch=getchar();getchar();
		int l=read(),r=read(),v=read();
		if(ch=='M'){
			for(register int i=l;i<=r;i++){
				a[i]+=v;
			}
		}
		else{
			int arc(0);
			for(register int i=l;i<=r;i++){
				if(a[i]>=v){
					arc++;
				}
			}
			write(arc),putchar('\n');
		}
	}
	return 0;
} 

OJ得66分,luogu得0分

然后怎么分块呢

预处理

打个板子捏

记录每个块的左右边界和\(pos[i]\)记录对应位置

	n=read(),m=read();
	t=sqrt(n);
	for(register int i=1;i<=n;i++){
		a[i]=read();
	}
	for(register int i=1;i<=t;i++){
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n){
		t++,L[t]=R[t-1]+1,R[t]=n;
	}
	for(register int i=1;i<=t;i++){
		for(register int j=L[i];j<=R[i];j++){
			pos[j]=i;
		}
	}

查询

对于查询的时候因为不能对中间一块一个一个一个得遍历找>=v の数有多少

所以可以给这个块内的数组排个序(满足单调),用二分查找出初始点,然后用右区间减就行

例如一个块内元素有

5 2 3 4 1

我们将他升序排列

  1 2 3 4 5
L[i]     R[i]

例如找>=2的数

二分找到2是 \(L[i]+1\) 然后用\(R[i]-(L[i]+1)+1\)

\(R[i]-\)二分答案\(+1\)

对于不足整块即左右两边的数组可以用暴力搜索

对于\(pos[l]=pos[r]\)的部分特判暴力搜索就行

inline static int testify(int l,int r,int v){
	int arc(0),p=pos[l],q=pos[r];
	if(p==q){
		for(register int i=l;i<=r;i++){
			if(add[p]+a[i]>=v) arc++;
		}
		return arc;
	}
	else{
		for(register int i=l;i<=R[p];i++){
			if(add[p]+a[i]>=v) arc++;
		}
		for(register int i=L[q];i<=r;i++){
			if(add[q]+a[i]>=v) arc++;
		}
		for(register int i=p+1;i<=q-1;i++){
			int ll=L[i],rr=R[i]+1,mid(0);
			while(ll<rr){
				mid=(ll+rr)>>1;
				if(d[mid]+add[i]<v){
					ll=mid+1;
				}
				else{
					rr=mid;
				}
			}
			arc+=(R[i]+1)-ll;
		}
	}
	return arc;
}

修改

板子但是加入一个新数组\(d[i]\)对于原来\(a[i]\)的块进行\(sort\)升序排列

对于中间块用\(add[i]\)进行标记

inline void SORT(int now){
	for(register int i=L[now];i<=R[now];i++){
		d[i]=a[i];
	}
	sort(d+L[now],d+R[now]+1);
}
inline static void change(int l,int r,int v){
	int p=pos[l],q=pos[r];
	if(p==q){
		for(register int i=l;i<=r;i++){
			a[i]+=v;
		}
		SORT(p);
	}
	else{
		for(register int i=l;i<=R[p];i++){
			a[i]+=v;
		}
		SORT(p);
		for(register int i=L[q];i<=r;i++){
			a[i]+=v;
		}
		SORT(q);
		for(register int i=p+1;i<=q-1;i++){
			add[i]+=v;
		}
	}
}

完结撒花,附上\(AC\)代码

注意\(d[i]\)重排列数组要在 \(main\)函数 初始化一下捏

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1000114;
inline int read(){
	int f(1),x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f*x;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int n,m,a[N],t,L[N],R[N],d[N],pos[N],add[N];
inline void SORT(int now){
	for(register int i=L[now];i<=R[now];i++){
		d[i]=a[i];
	}
	sort(d+L[now],d+R[now]+1);//d+L[now]是对应块の左端点,
   //数组元素个数为R[now]-L[now]+1
   //所以d+L[now]+(R[now]-L[now]+1)=d+R[now]+1是sort的右边
}
inline static void change(int l,int r,int v){
	int p=pos[l],q=pos[r];
	if(p==q){
		for(register int i=l;i<=r;i++){
			a[i]+=v;
		}
		SORT(p);
	}
	else{
		for(register int i=l;i<=R[p];i++){
			a[i]+=v;
		}
		SORT(p);
		for(register int i=L[q];i<=r;i++){
			a[i]+=v;
		}
		SORT(q);
		for(register int i=p+1;i<=q-1;i++){
			add[i]+=v;
		}
	}
}
inline static int testify(int l,int r,int v){
	int arc(0),p=pos[l],q=pos[r];
	if(p==q){
		for(register int i=l;i<=r;i++){
			if(add[p]+a[i]>=v) arc++;
		}
		return arc;
	}
	else{
		for(register int i=l;i<=R[p];i++){
			if(add[p]+a[i]>=v) arc++;
		}
		for(register int i=L[q];i<=r;i++){
			if(add[q]+a[i]>=v) arc++;
		}
		for(register int i=p+1;i<=q-1;i++){
			int ll=L[i],rr=R[i]+1,mid(0);
			while(ll<rr){
				mid=(ll+rr)>>1;
				if(d[mid]+add[i]<v){
					ll=mid+1;
				}
				else{
					rr=mid;
				}
			}
			arc+=(R[i]+1)-ll;
		}
	}
	return arc;
}
main(void){
	n=read(),m=read();
	t=sqrt(n);
	for(register int i=1;i<=n;i++){
		a[i]=read();
	}
	for(register int i=1;i<=t;i++){
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n){
		t++,L[t]=R[t-1]+1,R[t]=n;
	}
	for(register int i=1;i<=t;i++){
		for(register int j=L[i];j<=R[i];j++){
			pos[j]=i;
		}
	}
	for(register int i=1;i<=t;i++){
		SORT(i);
	}
	while(m--){
		char ch;
		cin>>ch;
		int l=read(),r=read(),v=read();
		if(ch=='M'){
			change(l,r,v);
		}
		else if(ch=='A'){
			write(testify(l,r,v)),putchar('\n');
		}
	}
	return 0;
} 

闲话

你这还不算什么,上次在学校食堂吃饭,突然看到饭里有一团红色????,我以为是鞭炮,准备吓一吓同学,好家伙,原来是核弹????,因为我是c语言大佬,所以我直接用程序黑了核弹程序????,后来全校都知道了,校长都吓的给我磕头????,我也没那么强吧,也就180开捷豹抽中华????,上次我在公交车上玩原神 m,有人叫我op,我当时就把他打了一顿????,他顺手拿起一根20cm粗的钢管打我,然后我把钢管折断了,把他打得青一块紫一块的,让他住了好几天院????????,后来他出院了,给我赔礼道歉,请我给了杯白水,我一喝,好家伙,原来是硫酸????,那钻心的疼让我一生难忘,还好我是体育生,跑得快 m,硫酸都被我甩出来了????,腿脚还利落,只是胃没了,在医院,医生测量了下我的体温,好家伙一百多度????,本来是不疼的,结果庸医误诊说修养就好了,我疼得受不了了????,就自己拿502胶胶了一下,一会就没事了????,好家伙502胶里有核弹,当时瞬间爆炸把我都炸到M87星云了????,幸亏我是体育生,没什么感觉,就擦破了点皮????,跑了差不多几个月吧,才回到地球,刚回到地球就看到地上有一块雷酸汞,我以为是薄荷糖,就拿来吃,当时嘴巴里突然一声巨响????????,据说那是雷酸汞爆炸的声音,当时我就只记得两眼一黑,昏过去了????,据说大脑小脑都炸飞了,还好我是体育生,复活赛只打了一个月就复活了????,还顺便学会了影分身十字斩结果释放了一地球的陈睿和孙笑川????,幸好我是体育生,玩了几天原农粥就没事了????,后来陈睿还送了我个自动变形劲爆火辣小火车,好像还是必胜客原神里的初音未来联名的,是限定的,已经绝版了????,现在想起来我一直在疑惑为什么没讹陈睿几十万????

posted @ 2023-05-25 11:21  超绝最可爱天使酱  阅读(105)  评论(2)    收藏  举报