[BZOJ3343]教主的魔法
[BZOJ3343]教主的魔法
试题描述
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、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的英雄数。
输入示例
5 3 1 2 3 4 5 A 1 5 4 M 3 5 1 A 1 5 4
输出示例
2 3
数据规模及约定
对30%的数据,N≤1000,Q≤1000。
对100%的数据,N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000。
题解
这题询问较少,但 N 比较大,可以往 O(Q√n) 复杂度的算法这个方向去想,况且这个题目用数据结构很难维护,所以就会想到分块。
想到分块之后就很简单了,每个块内部排一遍序,整块查询时可以二分,边上两块就可以暴力处理了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 1000010 #define maxb 1010 int n, q, A[maxn], Blo[maxn], st[maxb], en[maxb], addv[maxb], cb, pos[maxn]; void update(int l, int r, int val) { int bl = pos[l], br = pos[r]; if(br - bl < 2) { for(int i = st[bl]; i <= en[br]; i++) A[i] += addv[pos[i]]; addv[bl] = addv[br] = 0; for(int i = l; i <= r; i++) A[i] += val; for(int i = st[bl]; i <= en[br]; i++) Blo[i] = A[i]; sort(Blo + st[bl], Blo + en[bl] + 1); sort(Blo + st[br], Blo + en[br] + 1); return ; } for(int i = st[bl]; i <= en[bl]; i++) A[i] += addv[bl]; addv[bl] = 0; for(int i = st[br]; i <= en[br]; i++) A[i] += addv[br]; addv[br] = 0; for(int i = l; i <= en[bl]; i++) A[i] += val; for(int i = st[br]; i <= r; i++) A[i] += val; for(int i = st[bl]; i <= en[bl]; i++) Blo[i] = A[i]; sort(Blo + st[bl], Blo + en[bl] + 1); for(int i = st[br]; i <= en[br]; i++) Blo[i] = A[i]; sort(Blo + st[br], Blo + en[br] + 1); for(int i = bl + 1; i <= br - 1; i++) addv[i] += val; return ; } int query(int l, int r, int val) { int bl = pos[l], br = pos[r], ans = 0; if(br - bl < 2) { for(int i = st[bl]; i <= en[br]; i++) A[i] += addv[pos[i]], Blo[i] += addv[pos[i]]; addv[bl] = addv[br] = 0; for(int i = l; i <= r; i++) ans += (A[i] >= val); return ans; } for(int i = st[bl]; i <= en[bl]; i++) A[i] += addv[bl], Blo[i] += addv[bl]; addv[bl] = 0; for(int i = st[br]; i <= en[br]; i++) A[i] += addv[br], Blo[i] += addv[br]; addv[br] = 0; for(int i = l; i <= en[bl]; i++) ans += (A[i] >= val); for(int i = st[br]; i <= r; i++) ans += (A[i] >= val); for(int i = bl + 1; i <= br - 1; i++) { int x = lower_bound(Blo + st[i], Blo + en[i] + 1, val - addv[i]) - Blo; if(Blo[x] >= val - addv[i]) x--; if(en[i] > x) ans += en[i] - x; } return ans; } int main() { n = read(); q = read(); int t = (int)(sqrt(n) + .5); for(int i = 1; i <= n; i++) { A[i] = Blo[i] = read(); int ni = (i - 1) / t + 1; cb = ni; if(!st[ni]) st[ni] = i; en[ni] = i; pos[i] = ni; } for(int i = 1; i <= cb; i++) sort(Blo + st[i], Blo + en[i] + 1); while(q--) { char tp = Getchar(); while(!isalpha(tp)) tp = Getchar(); int ql = read(), qr = read(), val = read(); if(tp == 'M') update(ql, qr, val); if(tp == 'A') printf("%d\n", query(ql, qr, val)); } return 0; }
教主的这个魔法能不能在我身上用一下啊 QAQ