【题目链接】
LOJ #6278. 数列分块入门 2
【解析】
查询一个数在区间内的排名,可以用树套树、分块解决。分块虽然效率较低,但实现简单、且常数较小。而且可以支持区间修改。
对于每个块,将其在内部排序。查询时块内二分、边角暴力;修改时块内打标记、边角暴力修改后将其所在块重新排序。
【代码】
#include<iostream>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n, a[50010];
int len, b[50010], addv[50010];
vector<int> v[510];
void maintain(int x)
{
v[x].clear();
for(int i = (x - 1) * len + 1; i <= min(x * len, n); i++)
v[x].push_back(a[i]);
sort(v[x].begin(), v[x].end() );
}
void addb(int l, int r, int c)
{
for(int i = l; i <= min(b[l] * len, r); i++)
a[i] += c;
maintain(b[l]);
if(b[l] != b[r])
{
for(int i = (b[r] - 1) * len + 1; i <= r; i++)
a[i] += c;
maintain(b[r]);
}
for(int i = b[l] + 1; i < b[r]; i++)
addv[i] += c;
}
int query(int l, int r, int c)
{
int ans = 0;
for(int i = l; i <= min(b[l] * len, r); i++)
{
if(a[i] + addv[b[l] ] < c)
ans++;
}
if(b[l] != b[r])
{
for(int i = (b[r] - 1) * len + 1; i <= r; i++)
if(a[i] + addv[b[r] ] < c)
ans++;
}
for(int i = b[l] + 1; i < b[r]; i++)
{
int x = c - addv[i];
ans += lower_bound(v[i].begin(), v[i].end(), x) - v[i].begin();
}
return ans;
}
int main()
{
cin >> n, len = sqrt(n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
b[i] = (i - 1) / len + 1;
v[b[i] ].push_back(a[i]);
}
for(int i = 1; i <= b[n]; i++)
sort(v[i].begin(), v[i].end() );
for(int i = 1; i <= n; i++)
{
int opt, l, r, c;
scanf("%d%d%d%d", &opt, &l, &r, &c);
if(opt == 0)
addb(l, r, c);
else
printf("%d\n", query(l, r, c * c) );
}
return 0;
}