数数(代码源每日一题)
数数(离线树状数组处理可持久化问题)
题意
给定 \(n(1<=n<=10^5)\) 长度的数组 \(a_i(0<=a_i<=10^9)\), 进行 \(m\) 次询问,格式为 \(l,r,v\),每次询问 \(l<=i<=r\) ,有多少 \(a_i<=v\)
数数 - 题目 - Daimayuan Online Judge
思路
可先用前缀和的思想,每次询问的答案为 前 \(r\) 个数中小于等于 \(v\) 的个数 - 前 \(l-1\) 个数中小于等于 \(v\) 的个数
因此可以用可持久化线段树来处理
然而本题中,对于每次询问,大于 \(v\) 的元素无需考虑
因此也可将询问的 \(v\) 从小到大排序,每次只将小于等于 \(v\) 的元素插入树状数组中,也可用 \(nlogn\) 复杂度解决
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
int n, m, ans[N], tr[N], num;
vector<int> G[N];
vector<int> alls;
PII lastp[N];
struct Query
{
int l, r, v, id;
bool operator<(const Query &x) const
{
return v < x.v;
}
}q[N];
void clean()
{
alls.clear();
for (int i = 0; i <= num; i++)
{
tr[i] = 0;
G[i].clear();
}
}
int find(int x)
{
return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;
}
int lowbit(int x)
{
return x & -x;
}
void add(int idx)
{
for (int i = idx; i <= num; i += lowbit(i))
tr[i] += 1;
}
int sum(int idx)
{
int res = 0;
for (int i = idx; i; i -= lowbit(i))
res += tr[i];
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
alls.push_back(x);
lastp[i] = {x, i};
}
for (int i = 1; i <= m; i++)
{
int l, r, x;
cin >> l >> r >> x;
q[i] = {l, r, x, i};
alls.push_back(x);
}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
num = alls.size();
for (int i = 1; i <= n; i++)
{
int x = find(lastp[i].first);
G[x].push_back(lastp[i].second);
}
sort(q + 1, q + m + 1);
int h = 0;
for (int i = 1; i <= m; i++)
{
int v = q[i].v, l = q[i].l, r = q[i].r, id = q[i].id;
v = find(v);
for (int j = h+1; j <= v; j++)
{
for (auto k : G[j])
add(k);
}
h = v;
ans[id] = sum(r) - sum(l-1);
}
for (int i = 1; i <= m; i++)
cout << ans[i] << " ";
cout << endl;
clean();
}
return 0;
}