TOJ 4105 Lines Counting (树状数组)

题意:给定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\n", ans[i]);
    }
    return 0;
}

 

posted @ 2015-05-10 17:34  beisong  阅读(180)  评论(0编辑  收藏  举报