題目描述
教主最近學會了一種神奇的魔法,能夠使人長高。於是他準備演示給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的英雄數。
思路:我們用分塊來寫這道題,分好塊後將塊內的元素排序,然後記錄一下位置,對於修改操作來說,我們可以用直接修改,對於查詢操作的話端點的塊暴力找,中間的由於元素有序,我們可以二分來找,然後計算出該塊的數量,就可以得到答案了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
struct node
{
ll val;
int id;
bool operator < (const node &r) const
{
return val < r.val;
}
}a[maxn];
ll ad[maxn], ID[maxn]; //sum[i]第i塊的和,ad[i]第i塊的增量
ll L[maxn], R[maxn], pos[maxn]; //每段左右端點和每個位置屬於哪一段
int n, m, k; //k個塊
int read()
{
int x = 0, w = 1;
char ch = 0;
while(ch < '0' || ch > '9')
{
if(ch == '-')
w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * w;
}
ll slove(int l, int r, ll d, ll x) //二分查找
{
int p = r + 1, pp = r;
while(l <= r)
{
int mid = (l + r) >> 1;
if(a[mid].val + x >= d) r = mid - 1;
else l = mid + 1;
}
return pp - r;
}
void add(int l, int r, ll d) //區間[l, r]加上d
{
int p = pos[l], q = pos[r];
if(p == q) //同塊
{
for(int i = l; i <= r; ++i) a[ID[i]].val += d;
sort(a + L[p], a + R[p] + 1);
for(int i = L[p]; i <= R[p]; ++i)
ID[a[i].id] = i;
}
else
{
for(int i = p + 1; i <= q - 1; ++i) ad[i] += d;
for(int i = l; i <= R[p]; ++i) a[ID[i]].val += d;
for(int i = L[q]; i <= r; ++i) a[ID[i]].val += d;
sort(a + l, a + L[p] + 1);
sort(a + R[q], a + r + 1);
for(int i = L[p]; i <= R[p]; ++i) ID[a[i].id] = i;
for(int i = L[q]; i <= R[q]; ++i) ID[a[i].id] = i;
}
}
ll ask(int l, int r, ll d)
{
int p = pos[l], q = pos[r];
ll ret = 0;
if(p == q)
{
for(int i = l; i <= r; ++i)
if(a[ID[i]].val + ad[p] >= d)
++ret;
}
else
{
for(int i = l; i <= R[p]; ++i) if(a[ID[i]].val + ad[p] >= d) ++ret;
for(int i = L[q]; i <= r; ++i) if(a[ID[i]].val + ad[q] >= d) ++ret;
for(int i = p + 1; i <= q - 1; ++i)
ret += slove(L[i], R[i], d, ad[i]);
}
return ret;
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; ++i) a[i].val = read(), a[i].id = i;
k = sqrt(n); //分成k塊
for(int i = 1; i <= k; ++i)
{
L[i] = (i - 1) * k + 1;
R[i] = i * k;
}
if(R[k] < n) //最後有一小塊
{
++k;
L[k] = R[k - 1] + 1;
R[k] = n;
}
for(int i = 1; i <= k; ++i)
{
sort(a + L[i], a + R[i] + 1); //對每一段排序
for(int j = L[i]; j <= R[i]; ++j)
{
pos[j] = i;
ID[a[j].id] = j; //記錄位置
}
}
while(m--)
{
char op[3];
int l, r;
ll d;
scanf("%s", op);
l = read(), r = read(), d = read();
if(op[0] == 'M')
{
add(l, r, d);
}
else if(op[0] == 'A')
printf("%lld\n", ask(l, r, d));
}
return 0;
}