CodeForces - 369E Valera and Queries(树状数组)

题目大意:给出N条线段,有M个询问,询问的内容是,k个点,共落在多少条所给线段上

解题思路:这次参考了一下别人的题解。首先,很难直接从正面判断落在几条线段上,因为线段是会重叠的,且,有可能有多个点落在同一条线段上的
既然正面难以思考的话,那就用反面,判断一下有多少条所给线段会被不包含所有点的线段给包含在内,假设所给的点是1,4,7,那就判断[2,3],[5,6],[8,Max-1]这三个线段,共包含多少条所给线段,只要被包含在内,那么所给的点必然不会落在所给线段上,所以最后只需要用N-包含的线段即可
现在的问题是统计一下线段了。首先,先处理出所有的不包含线段,还要给每个线段打上标记,看是属于哪个问题的
接着将所给线段和处理出来的线段排个序,最容易被包含的先处理,最容易被包含的,就是那些l比较大,r比较小的
然后树状数组维护的是前缀和,表示的是到该点,有多少条线段被包含里面
因为排序的时候是l大的先处理,所以后续的线段的l都会小于等于前面的线段的,所以依此处理是可以的

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
const int M = 300010;

struct Node {
    int l, r, id;
    Node() {}
    Node(int l, int r, int id): l(l), r(r), id(id){}
}node[N];
int bit[N], ans[M];
int n, m;

inline int lowbit(int x) {
    return x & (-x);
}

void Modify(int x) {
    while (x < N) {
        bit[x]++;
        x += lowbit(x);
    }
}

int Query(int x) {
    int ans = 0;
    while (x) {
        ans += bit[x];
        x -= lowbit(x);
    }
    return ans;
}

int cmp(const Node &a, const Node &b) {
    if (a.l == b.l) {
        if (a.r == b.r) return a.id < b.id;
        return a.r < b.r;
    }
    return a.l > b.l;
}

void solve() {
    memset(ans, 0, sizeof(ans));
    memset(bit, 0, sizeof(bit));
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &node[i].l, &node[i].r);
        node[i].id = 0;
    }
    int cnt = n;
    int num, p1, p2;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &num, &p1);
        if (p1 > 1) node[++cnt] = Node(1, p1 - 1, i);
        p2 = p1;
        for (int j = 2; j <= num; j++) {
            scanf("%d", &p2);
            if (p1 + 1 <= p2 - 1) node[++cnt] = Node(p1 + 1, p2 - 1, i);
            p1 = p2;
        }
        node[++cnt] = Node(p2 + 1, N - 7, i);
    }

    sort(node + 1, node + 1 + cnt, cmp);
    for (int i = 1; i <= cnt; i++) {
        if (node[i].id) ans[node[i].id] += Query(node[i].r);
        else Modify(node[i].r);
    }

    for (int i = 1; i <= m; i++)
        printf("%d\n", n - ans[i]);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF) solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值