bestcode的题目感觉挺巧妙的,给一些区间(n < 3*10^5),给很多组点(m < 3 * 10^5 总点数sum < 3*10^5),求每组点中有多少个区间包含了至少该组点中的一个点。
尝试了两种裸的线段树都超时了。
第一种是 对于每组点,首先加点到树状数组,然后对于每个区间求该区间中的点数,大于0则计数。时间为O(n*m) 跪了。。
第二种是 用lazy线段树,每个节点放覆盖该节点表示范围的区间的标号。这样初始化将所有区间加入线段树。对一每个组查询每个点,从根节点范围开始查询该点路径上经过的区间,用set保证唯一。时间主要取决于区间在节点范围上的分布。依然跪了。。
正解是:
首先问题反相思考,求一个点都没有覆盖的区间有多少个。
这样的区间 l 和 r 都包含在两个相邻的点的区间内,这样就可以按 r 排序,然后向树状数组里加 l ,两个相邻点范围[a, b]内包含的区间即为:r在该范围内的点数-r在该范围且l在[0, a]。这样树状数组里记录 l 的累加就可以了
然后题目要全部离线,一遍求出所有解,用类似链表的方式记录和该点同组的前一个点是谁。
下面是代码了。。。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <climits> #include <iostream> #include <algorithm> #include <vector> #include <map> #include <set> #include <queue> #define LL long long #define PB push_back #define N 1000010 using namespace std; struct intv{ int a, b; bool operator<(const intv& x) const { return b < x.b; } }; struct point{ int a, pre, ind; point(int _a = 0, int _pre = 0, int _ind = 0): a(_a), pre(_pre), ind(_ind) { } void set(int _a, int _pre, int _ind) { a = _a; pre = _pre; ind = _ind; } bool operator<(const point& x) const { return a < x.a; } }; point p[N]; int np, countt[N]; intv in[N]; int t[N]; inline int lowbit(int p) { return p & (-p); } void add(int p, int x) { while(p < N) { t[p] += x; p += lowbit(p); } } int query(int p) { int re = 0; while(p) { re += t[p]; p -= lowbit(p); } return re; } int query(int l, int r) { return query(r) - query(l-1); } int main() { int n, m; while(scanf("%d%d", &n, &m) != EOF) { memset(t, 0, sizeof(t)); for(int i=0; i<n; ++i) { scanf("%d%d", &in[i].a, &in[i].b); } sort(in, in+n); np = 0; p[np++].set(0, 0, 0); for(int i=0; i<m; ++i) { int num; scanf("%d", &num); int pre = 0; for(int j=0; j<num; ++j) { int a; scanf("%d", &a); p[np++].set(a, pre, i); pre = a; } p[np++].set(N-1, pre, i); countt[i] = 0; } sort(p, p+np); int usep = 1, usei = 0; while(usep < np) { while( usei < n && in[usei].b < p[usep].a ) { add(in[usei].a, 1); ++usei; } // printf("%d %d %d %d\n", p[usep].ind, p[usep].pre, p[usep].a, query(p[usep].pre+1, p[usep].a)); countt[ p[usep].ind ] += query(p[usep].pre+1, p[usep].a); ++usep; } for(int i=0; i<m; ++i) { printf("%d\n", n - countt[i]); } } return 0; }