题目大意:给出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;
}