随便说点
这题开始时候想的是线段树(不知道为什么这两天的题我都想到要用线段树来做),后来想想是错的,但还是强行冲了一发,居然AAA了。后来出了一个后台数据,交到官方去了,不知道他们什么时候会加进去。
再讲一下题,这题用的是树状数组,当然也可以用线段树,算是让我这个数据结构白痴学习了一下数据结构的内容。
题意
给出一个数组,有qqq次询问,每次询问输出某个区间内小于等于xxx的数的数量。
题解
首先我们先明确一个前缀和的思想。题目问的是[l,r][l,r][l,r]区间内有多少数字符合条件,我们可以将其转化为前xxx个中有多少个点符合要求,然后通过sum(r)−sum(l−1)sum(r)-sum(l-1)sum(r)−sum(l−1)来得出答案。
然后是sum(x)sum(x)sum(x)怎么求?
sumsumsum是什么?我们可以认为是一种前缀和,那我们该如何维护这个前缀和呢?我们先把所有的值离散化,根据值的大小从小到大排序,同时我们把所有的询问的值也都从小到大排序,同时遍历两个数组,标记所有比当前询问小的值,因为询问是从小到大排序的,所以之前标记过的节点一定比当前询问要小。于是这个问题就被转换为了一个单点修改区间求和的题目,然后我们直接套树状数组板子就行了。
#include<bits/stdc++.h>
using namespace std;
int main() {
#ifdef ACM_LOCAL
freopen("data.in", "r", stdin);
#endif
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _;
cin >> _;
while (_--) {
int n, q;
cin >> n >> q;
vector<pair<int, int>> pir(n + 1);//存放数字
for (int i = 1; i <= n; i++) cin >> pir[i].first, pir[i].second = i;
sort(pir.begin() + 1, pir.end());
struct matter {
int l, r, h, t;
};
vector<matter> vec;//存放事件
for (int i = 1; i <= q; i++) {
int l, r, h;
cin >> l >> r >> h;
vec.push_back((matter) {l, r, h, i});
}
sort(vec.begin(), vec.end(), [](matter a, matter b) { return a.h < b.h; });
vector<int> tree(n + 1);//树状数组
vector<int> ans(q + 1);//存放答案
vector<int> flag(n + 1);//单点修改
auto lowbit = [](int x) -> int { return x & (-x); };
auto add = [&n, &lowbit, &tree](int x, int k) -> void { for (int i = x; i <= n; i += lowbit(i)) tree[i] += k; };
auto sum = [&lowbit, &tree](int x) -> int {
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) ans += tree[i];
return ans;
};
int p = 0;
for (auto v: vec) {
int l = v.l, r = v.r;
int h = v.h, t = v.t;
while (p < n && h >= pir[p + 1].first)
add(pir[++p].second, 1);
ans[t] = sum(r) - sum(l - 1);
}
for (int i = 1; i <= q; i++) cout << ans[i] << " ";
cout << endl;
}
return 0;
}