题意:给定N条线段,每条线段的两个端点L和R都是整数。然后给出M个询问,每次询问给定两个区间[L1,R1]和[L2,R2],问有多少条线段满足:L1≤L≤R1 , L2≤R≤R2 ?
题解,采用离线做法,先将所有线段和询问区间全部保存。然后将每个询问[L1,R1][L2,R2]拆分成两个,L1-1, [L2,R2]和 R1,[L2,R2]。x, [L2,R2]表示起点 L <= x,终点 R满足 L2 <= R <= R2的线段条数,则每个询问的答案就是 R1,[L2,R2]的值 - L1-1, [L2,R2]的值。那么下面就需要查询x,[L2,R2]的值了。
将所有线段按照起点排序;将所有新生成的询问按照x排序。之后是一遍扫描,对每个询问,循环直到线段起点L>x,否则树状数组中线段终点位置处值加1。此时计算树状数组第R2位置的前缀和减去L1-1位置处的前缀和即为答案。详见代码:
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define N 100100 struct Line{ int s, t; bool operator < (const Line a) const { return s < a.s; } }p[N]; struct Range{ int s, id; Line t; bool operator < (const Range a) const { return s < a.s; } }rg[2*N]; int v[N]; int lb(int x){return x&-x;} void add(int id){ while(id < N){ v[id]++; id += lb(id); } } int get(int id){ if(id == 0) return 0; return v[id]+get(id-lb(id)); } int ans[N]; int main(){ int n, m, i, j; while(~scanf("%d", &n)){ for(i = 0; i < n; i++) scanf("%d %d", &p[i].s, &p[i].t); scanf("%d", &m); int s1, t1, s2, t2, c = 0; for(i = 0; i < m; i++){ scanf("%d %d %d %d", &s1, &t1, &s2, &t2); rg[c].s = s1-1; rg[c].t.s = s2, rg[c].t.t = t2; rg[c].id = c++; rg[c].s = t1; rg[c].t.s = s2, rg[c].t.t = t2; rg[c].id = c++; } sort(p, p+n); sort(rg, rg+c); memset(ans, 0,sizeof(ans)); memset(v, 0, sizeof(v)); for(int i = 0, j = 0; i < c; i++) { while(j < n && p[j].s <= rg[i].s) add(p[j++].t); int tm = get(rg[i].t.t) - get(rg[i].t.s-1); if(rg[i].id&1) ans[rg[i].id/2] += tm; else ans[rg[i].id/2] -= tm; } for(int i = 0; i < m; i++) printf("%d ", ans[i]); } return 0; }