title: 牛客小白9 换个角度思考(离线+树状数组)
date: 2018-11-29 15:25:18
tags: [离线,树状数组]
categories: ACM
题目描述
给定一个序列,有多次询问,每次查询区间里小于等于某个数的元素的个数
即对于询问 (l,r,x),你需要输出 的值
其中 [exp] 是一个函数,它返回 1 当且仅当 exp 成立,其中 exp 表示某个表达式
输入描述:
第一行两个整数n,m
第二行n个整数表示序列a的元素,序列下标从1开始标号,保证1 ≤ ai ≤ 105
之后有m行,每行三个整数(l,r,k),保证1 ≤ l ≤ r ≤ n,且1 ≤ k ≤ 105
输出描述:
对于每一个询问,输出一个整数表示答案后回车
输入
5 1
1 2 3 4 5
1 5 3
输出
3
备注:
数据范围
1 ≤ n ≤ 105
1 ≤ m ≤ 105
思路
- 将原数组和要查询的K值升序排列,这样能保证前面小的数的贡献对后面也有影响
- 然后用树状数组维护
#include <bits/stdc++.h>
#define N 100006
#define mem(a, b) memset(a, b, sizeof(a))
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
using namespace std;
struct ac{
int val, id, l, r;
}a[N], b[N];
int ans[N], c[N];
int n, m;
void update(int x, int d) {
while (x <= n) {
c[x] += d;
x += lowbit(x);
}
}
int query(int x) {
int ans = 0;
while (x) {
ans += c[x];
x -= lowbit(x);
}
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
scanf("%d %d", &n, &m);
// 读入数组,并记录序号
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i].val);
a[i].id = i + 1;
}
// 读入查询,记录序号
for (int i = 0; i < m; ++i) {
scanf("%d %d %d", &b[i].l, &b[i].r, &b[i].val);
b[i].id = i + 1;
}
// 分别升序排列
sort(a, a + n, [&](const ac &x, const ac &y){
return x.val < y.val;
});
sort(b, b + m, [&](const ac &x, const ac &y){
return x.val < y.val;
});
// j枚举原数组 1-n 的数字
int j = 0;
for (int i = 0; i < m; ++i) {
// 存在小于当前查询的K值,就树状数组更新贡献
while (j < n && a[j].val <= b[i].val) {
update(a[j].id, 1);
++j;
}
// 将答案按照序号保存在数组中
ans[b[i].id] = query(b[i].r) - query(b[i].l - 1);
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}