【洛谷 2801】教主的魔法

【题目】

传送门

题目描述:

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

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

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

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

输入格式:

1 1 1 行为两个整数 n n n q q q q q q 为问题数与教主的施法数总和。

2 2 2 行有 n n n 个正整数,第 i i i 个数代表第 i i i 个英雄的身高。

3 3 3 到第 q + 2 q+2 q+2 行每行有一个操作:

  1. 若第一个字母为 “M”,则紧接着有三个数字 l l l r r r w w w。表示对闭区间 [ l l l , , , r r r ] 内所有英雄的身高加上 w w w
  2. 若第一个字母为 “A”,则紧接着有三个数字 l l l r r r c c c。询问闭区间 [ l l l , , , r r r ] 内有多少英雄的身高大于等于 c c c

输出格式:

对每个 “A” 询问输出一行,仅含一个整数,表示闭区间 [ l l l , , , r r r ] 内身高大于等于 c c c 的英雄数。

样例数据:

输入
5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4

输出
2
3

说明:

【输入输出样例说明】

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

【数据范围】

30 % 30\% 30% 的数据, n ≤ 1000 n≤1000 n1000 q ≤ 1000 q≤1000 q1000

100 % 100\% 100% 的数据, n ≤ 1000000 n≤1000000 n1000000 q ≤ 3000 q≤3000 q3000 1 ≤ w ≤ 1000 1≤w≤1000 1w1000 1 ≤ c ≤ 1 , 000 , 000 , 000 1≤c≤1,000,000,000 1c1,000,000,000


【分析】

分块基础题

区间加就是基本操作,主要是询问怎么做

对于每个块,除了用 a i a_i ai 维护原序列外,还要用一个数组维护对每个块内排序后的序列

例如,对于序列 5 , 4 , 3 , 2 , 1 5,4,3,2,1 5,4,3,2,1,排序后的序列就是 4 , 5 , 2 , 3 , 1 4,5,2,3,1 4,5,2,3,1

有了这个之后,就可以用 lower_bound 快速统计整块内的询问,对于散块直接暴力查询就行了

时间复杂度 O ( q n    log ⁡ 2 n ) O(q\sqrt n\;\log_2\sqrt n) O(qn log2n ),足以解决这道题


【代码】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define W 1005
#define N 1000005
#define Sqrtn 1005
using namespace std;
char op[5];
int n,q,S,num,a[N],A[N],L[N],R[N],be[N],add[Sqrtn];
void rebuild(int x)
{
	int i;
	for(i=L[x];i<=R[x];++i)  A[i]=a[i];
	sort(A+L[i],A+R[i]+1);
}
void Add(int l,int r,int x)
{
	int i;
	if(be[r]-be[l]<2)
	{
		for(i=l;i<=r;++i)  a[i]+=x;
		rebuild(be[l]),rebuild(be[r]);
		return;
	}
	for(i=l;i<=R[be[l]];++i)  a[i]+=x;
	for(i=L[be[r]];i<=r;++i)  a[i]+=x;
	for(i=be[l]+1;i<=be[r]-1;++i)  add[i]+=x;
	rebuild(be[l]),rebuild(be[r]);
}
int query(int l,int r,int x)
{
	int i,ans=0;
	if(be[r]-be[l]<2)
	{
		for(i=l;i<=r;++i)
		  if(a[i]+add[be[i]]>=x)
		    ans++;
		return ans;
	}
	for(i=l;i<=R[be[l]];++i)  if(a[i]+add[be[i]]>=x)  ans++;
	for(i=L[be[r]];i<=r;++i)  if(a[i]+add[be[i]]>=x)  ans++;
	for(i=be[l]+1;i<=be[r]-1;++i)  ans+=(R[i]+1-(lower_bound(A+L[i],A+R[i]+1,x-add[i])-A));
	return ans;
}
int main()
{
	int l,r,i,x;
	scanf("%d%d",&n,&q);
	S=sqrt(n);
	for(i=1;i<=n;++i)
	{
		be[i]=(i-1)/S+1;
		scanf("%d",&a[i]);
	}
	for(i=1;;++i)
	{
		num++;
		L[i]=(i-1)*S+1,R[i]=i*S;
		if(R[i]>=n)  {R[i]=n;break;}
	}
	memcpy(A,a,sizeof(A));
	for(i=1;i<=num;++i)
	  sort(A+L[i],A+R[i]+1);
	for(i=1;i<=q;++i)
	{
		scanf("%s%d%d%d",op,&l,&r,&x);
		if(op[0]=='M')  Add(l,r,x);
		else  printf("%d\n",query(l,r,x));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值