首页 > 其他分享 >Luogu P2801 教主的魔法(Loj 数列分块入门 2)

Luogu P2801 教主的魔法(Loj 数列分块入门 2)

时间:2023-05-22 19:57:58浏览次数:32  
标签:include Loj Luogu P2801 pos int 教主 英雄 身高

教主的魔法

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给 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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>

using namespace std;

int block, n, q;
//add[i]表示第i个块整个块要加的值 pos[i]表示第i个数所处的块
int add[5211314], a[5211314], pos[5211314];
vector<int> b[5210];//b[i]表示第i个块快速排序完后的顺序
string str;

inline void Add(int lift, int right, int val) {
	int l = pos[lift], r = pos[right];
	if (l == r) {
        //在一个块内直接暴力区间加
		b[l].clear();
		for (int i = lift; i <= right; ++ i) 
			a[i] += val;
		for (int i = (l - 1) * block + 1; i <= min(l * block, n); ++ i) 
			b[l].push_back(a[i]);
		sort(b[l].begin(), b[l].end());
	}
	else {
		b[l].clear();
		b[r].clear();
        //对每个块的值加val
		for (int i = l + 1; i <= r - 1; ++ i) {
			add[i] += val; 
		}
        //暴力修改左右未完全覆盖块的值
		for (int i = lift; i <= l * block; ++ i) {
			a[i] += val;
		}
		for (int i = (r - 1) * block + 1; i <= right; ++ i) {
			a[i] += val;
		}
        //将修改完的左右未完全覆盖块的值重新插入b里并排序
		for (int i = (l - 1) * block + 1; i <= min(l * block, n); ++ i) {
			b[l].push_back(a[i]);
		}
		for (int i = (r - 1) * block + 1; i <= min(r * block, n); ++ i) {
			b[r].push_back(a[i]);
		}
		sort(b[l].begin(), b[l].end());
		sort(b[r].begin(), b[r].end());
	}
	return;
}

inline int Ask(int lift, int right, int val) {
	int l = pos[lift], r = pos[right], ans = 0;
	if (l == r) {
        //左右端点在同一个块内直接暴力
		for (int i = lift; i <= right; ++ i) {
			if (a[i] + add[l] >= val) ans ++;
		}
		return ans;
	}
	else {
        //不在同一个块内则二分寻找每个块内第一个大于等于val的值的位置
		for (int i = l + 1, subscript; i <= r - 1; ++ i) {
			subscript = lower_bound(b[i].begin(), b[i].end(), val - add[i]) - b[i].begin();
            //防止没找到第一个大于等于val而返回最后一个值的下标
			if (b[i][subscript] >= val - add[i]) {
				ans += (block - subscript);
			}
		}
        //暴力左右两个未完全覆盖的块
		for (int i = lift; i <= l * block; ++ i) {
			if (a[i] + add[l] >= val) ans ++;
		}
		for (int i = (r - 1) * block + 1; i <= right; ++ i) {
			if (a[i] + add[r] >= val) ans ++;
		}
		return ans;
	}
}

int main() {
	cin >> n >> q;
	block = sqrt(n);//块长
	for (int i = 1; i <= n; ++ i) {
		cin >> a[i];
		pos[i] = (i - 1) / block + 1;
		b[pos[i]].push_back(a[i]);
	}
	for (int i = 1; i <= pos[n]; ++ i) { 
		sort(b[i].begin(), b[i].end());
	}
	for (int i = 1, l, r, w; i <= q; ++ i) {
		cin >> str;
		cin >> l >> r >> w;
		if (str == "A") cout << Ask(l, r, w) << endl;
		else Add(l, r, w);
	}
    return 0;
}

标签:include,Loj,Luogu,P2801,pos,int,教主,英雄,身高
From: https://www.cnblogs.com/jueqingfeng/p/17421555.html

相关文章

  • Luogu P5643 [PKUWC2018]随机游走
    题意给出一棵\(n\)结点树,从结点\(x\)出发,每次从当前点的所有边中选一条走过去,\(Q\)次询问给定一个点集\(S\),随机游走直到经过\(S\)中的每一个点至少一次的期望总步数,出发点\(x\)默认在开始时已经被经过。\(n\le18,Q\le5000\)解法萌新第一次见到这种题,感觉很神。......
  • Luogu P3978 [TJOI2015] 概率论
    定义\(f_i\)为\(i\)个节点组成的二叉树数量,\(g_i\)为\(i\)个节点组成的二叉树的叶子节点个数之和设当前\(i\)个节点组成的二叉树有\(a\)个叶子,容易发现分别删掉其中的\(1\)个叶子节点就能得到一个对应的\(i-1\)个节点的二叉树,总共会有\(a\)颗,可以发现每一个叶......
  • Luogu P5664 [CSP-S2019] Emiya 家今天的饭
    发现“每种主要食材至多在\(\lfloor\frac{k}{2}\rfloor\)个菜中被使用”有一个性质,在不合法的情况下绝对只有\(1\)个主要食材的个数\(>\lfloor\frac{k}{2}\rfloor\),因为\(k-\lfloor\frac{k}{2}\rfloor-1\le\lfloor\frac{k}{2}\rfloor\)然后就能发现算不合法......
  • 刷题笔记:Luogu P3743
    题目传送门Solution最多能将这些设备一起使用多久,显然答案满足单调性(如果\(x<y\)而不能使用\(x\)时间则一定不能使用\(y\)时间)通俗一点,就是前边的时间不满足则后边一定不满足,也就是局部答案舍弃性,考虑二分时间至于check怎么写呢?和奶牛晒衣服有异曲同工之妙,若设二分出来的时间......
  • 蒲公英(Loj 分块模板9)
    [Violet]蒲公英题目背景亲爱的哥哥:你在那个城市里面过得好吗?我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受......
  • Loj分块模板1
    #include<iostream>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>usingnamespacestd;intn,m,len;intpos[5211314],add[5211314],num[5211314];inlinevoidAdd(intLiftPos,intRightPos,intval......
  • 刷题笔记:Luogu P1083 借教室
    题目传送门让结果最接近\(s\)值,显然我们要二分\(w\),check的写法可以直接暴力模拟,如果check(mid)<s则将r右移(通过读公式可以知道\(w\)越小检验值\(y\)就越大)但是这样会TLE,再读一下柿子:\(y_i=\sum\limits_{j=l_i}^{r_i}[w_j\geW]\times\sum\limits_{j=l_i}^{r_i}[w_j\geW]......
  • 【CF1012E】【LOJ2818】Cycle Sort(并查集)
    Description给定一个⻓为nn的数列,你可以多次进行如下操作:选定kk个不同的下标i1,i2…iki1,i2......
  • luogu P8340 [AHOI2022] 山河重整
    题面传送门牛逼题。solution首先来推一推性质。假设我们现在有一个合法的集合,覆盖了\([1,S]\),显然新加进去的数\(i\)不能\(\geqS+2\),而如果\(\leqS+1\)那么\([1,i+S]\)显然可以被覆盖到。因此有一个\(O(n^2)\)的dp:设选到了第\(i\)个数,总和为\(j\),要求\(j\geq......
  • Luogu P5520 [yLOI2019] 青原樱
    考虑先不种花,先算出“花之间空位的可行方案数”\(tot\),乘上花的排列的贡献就能算出答案,即\(tot\timesm!\)为答案所以只需算出“花之间空位的可行方案数”能发现,设\(x_i\)为第\(i\)朵花与第\(i-1\)朵花之间空位的数量,其中设第\(0\)朵花在\(0\)的位置,则发现会有以......