题意:n个线段[Li, Ri], m次询问, 每次询问由cnt个点组成,输出包含cnt个点中任意一个点的线段的总数。
由于是无修改的,所以我们首先应该往离线上想, 不过我是没想出来。
首先反着做,先求不包含这个cnt个点的线段的总数, 那么不包含这些点的线段必然在cnt个点之间(这里需要再加两个点一个是0, 一个是MAX),
我们可以把所有线段按Ri 分类, 然后按右端点遍历,对于当前的线段可以在Li 处+1, 然后对于每一次询问中两个点(x, y)之间线段的个数,
只需要查询 左端点大于等于x的个数就好了,这里因为是按右端点从小到大遍历的,所以不用考虑用端点了。
发现, 大多数的离线处理都是先把其中的一维从小到大排序, 然后处理另一维。 这样相当于降维。
#include <bits/stdc++.h> const int maxn = 1e6 + ;
typedef std::pair <int, int> pii;
std::vector <int> seg[maxn], q[maxn];
std::vector <pii> rq[maxn];
namespace FenwickTree{
int arr[maxn];
void inc(int x, int d){
while (x < maxn){
arr[x] += d;
x += x & -x;
}
}
int query(int x){
int res = ;
while (x > ){
res += arr[x];
x -= x & -x;
}
return res;
}
int query(int ua, int ub){
return query(ub) - query(ua-);
}
}
int ans[maxn];
void init(){
memset(ans, , sizeof (ans));
for (int i = ; i < maxn; i++){
seg[i].clear();
q[i].clear();
rq[i].clear();
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
int n, m;
while (~ scanf ("%d%d", &n, &m)){
init();
for (int i = ; i < n; i++){
int ua, ub;
scanf("%d%d", &ua, &ub);
ua++, ub++;
seg[ub].push_back(ua);
}
for (int i = ; i < m; i++){
int cnt, p;
scanf ("%d", &cnt);
q[i].push_back();
while (cnt--){
scanf ("%d", &p);
p++;
q[i].push_back(p);
}
q[i].push_back(maxn-);
for (int j = ; j < q[i].size()-; j++){
int ua = q[i][j]+;
int ub = q[i][j+]-;
rq[ub].push_back(std::make_pair(ua, i));
}
}
for (int i = ; i < maxn; i++){
for (int j = ; j < seg[i].size(); j++){
int tmp = seg[i][j];
FenwickTree::inc(seg[i][j], );
}
for (int j = ; j < rq[i].size(); j++){
int idx = rq[i][j].second;
int tmp = rq[i][j].first;
ans[idx] += FenwickTree::query(rq[i][j].first, i);
}
}
for (int i = ; i < m; i++){
printf("%d\n", n - ans[i]);
}
}
return ;
}