题目传送门
思路
一眼主席树板子题,但是一看数据范围 \(n,m\le 2\times 10 ^ 6\),似了。
在线做法应该是似完了,考虑离线做法。
我们知道树状数组是可以做二维偏序的,大家应该都知道一个经典问题:对于一个序列,多次询问下标 \(\le a\) 且数值 \(\le b\) 的数的个数。
回到这道题,相比上面只多了一个左端点的限制当然还有数据范围,所以可以采用类似前缀和的方法,对于一个询问 \(\{l, r, x\}\),用 \([1, r]\) 的这种数的个数减去 \([1, l - 1]\) 中的即可。
那么可以将询问拆成两部分,一部分是左端点减一,一部分是右端点,同时还要记录符号,左端点是减,右端点是加。
接下来就可以像扫描线一样,将 \(pos\) 按从小到大排序,然后从左到右扫描一遍计算答案就行了。
时间复杂度 \((n\log {2\cdot 10 ^ 6} + m\log m + m\log n)\),若都与 \(n\) 同级就为 \(O(n\log n)\)。
\(\texttt{Code:}\)
#include <iostream>
#include <algorithm>
#define lowbit(x) x & -x
using namespace std;
const int N = 2000010;
int n, m;
int a[N];
struct BIT{
int c[N];
void add(int x, int y) {
for(; x < N; x += lowbit(x)) c[x] += y;
}
int ask(int x) {
int res = 0;
for(; x; x -= lowbit(x)) res += c[x];
return res;
}
}tr;
struct node{
int id, pos, x, sign;
bool operator< (const node &o) const {
return pos < o.pos;
}
}q[N << 1];
int tt;
int ans[N];
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
int l, r, x;
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &l, &r, &x);
q[++tt] = {i, l - 1, x, -1};
q[++tt] = {i, r, x, 1};
}
sort(q + 1, q + tt + 1);
int id = 1;
for(int i = 1; i <= tt; i++) {
while(id <= q[i].pos && id <= n) tr.add(a[id++], 1);
ans[q[i].id] += q[i].sign * tr.ask(q[i].x);
}
for(int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
return 0;
}
标签:le,P10814,log,int,题解,离线,pos,端点
From: https://www.cnblogs.com/Brilliant11001/p/18334341