数数
题目链接
数数
在给定 \(N\) 长的数组 \({A}\) 中进行 \(Q\) 次询问 \([L_i,R_i]\) 区间中不大于 \(H_i\) 的元素个数。
共包含 \(T\) 组数据。
输入格式
输入就像下面这样:
T
N Q
A1 A2 A3 ... AN
L1 R1 H1
L2 R2 H2
...
LQ RQ HQ
...
输出格式
\(T\) 组数据,每组都输出一行,包含 \(Q\) 个以空格分隔的整数,表示答案。
样例输入
1
10 3
0 5 2 7 5 4 3 8 7 7
3 9 6
4 6 0
2 4 2
样例输出
4 0 1
样例说明: \(A[3..9]=[2,7,5,4,3,8,7]\), 其中不大于 \(6\) 的元素数量为 \(4\)。
数据规模
\(1≤N,Q≤10^5\)
\(0≤A_i,H≤10^9\)
\(1≤L≤R≤N\)
数据保证 \(∑N,Q≤10^5\)
解题思路
树状数组,双指针
离线做法:记录数组的位置,按值的大小排序,同时记录询问的时间,按 \(h\) 排序,从前往后遍历每次询问,每次将不大于 \(h_i\) 的\(a_i\) 的位置放入树状数组中,然后统计前缀和,由于后面大于 \(h_i\) 的位置对答案没有影响,所以暂时不把那些对答案无影响的位置加上去
- 时间复杂度:\(O(nlogn+qlogq)\)
代码
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int t,tr[N],n,Q,res[N];
PII a[N];
struct qu
{
int l,r,h,id;
bool operator<(qu &o)const
{
return h<o.h;
}
}q[N];
int find(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void add(int x,int y)
{
for(;x<=n;x+=x&-x)tr[x]+=y;
}
int ask(int x)
{
int res=0;
for(;x;x-=x&-x)res+=tr[x];
return res;
}
int main()
{
for(cin>>t;t;t--)
{
memset(tr,0,sizeof tr);
cin>>n>>Q;
for(int i=1;i<=n;i++)
{
cin>>a[i].fi;
a[i].se=i;
}
sort(a+1,a+1+n);
for(int i=1;i<=Q;i++)
{
cin>>q[i].l>>q[i].r>>q[i].h;
q[i].id=i;
}
sort(q+1,q+1+Q);
for(int i=1,j=1;i<=Q;i++)
{
while(j<=n&&a[j].fi<=q[i].h)add(a[j++].se,1);
res[q[i].id]=ask(q[i].r)-ask(q[i].l-1);
}
for(int i=1;i<=Q;i++)cout<<res[i]<<' ';
puts("");
}
return 0;
}