题目大意
网格上有n个点(x,y),有q个询问,每次在(a,b)的右上角放一个向左和向下的栅栏,问栅栏围进了多少个点,每个栅栏碰到原来的栅栏或坐标轴就会结束。
1<=n,q<=100000
1<=a[i],b[i]<=10^9
1<=x[i],y[i]<=10^9
思路
考试时的我是很naive的,看错题了,以为一个点只能贡献一次答案,写了一个假的CDQ+线段树,样例不过。其实从样例就可以看出一个点可能多次贡献答案。
考虑扫描线,将点和栅栏从上往下、从左向右扫描,对坐标稍微处理一下,让询问比点先发生。
考虑这样一个关系,一个询问在另一个询问的右上方,如果右上方的时间靠前,则答案将贡献过去。我们向第一个这样的询问连边。这个用并查集维护。至于时间我们先不管。如何找到x在当前点右边的第一个呢?平衡树即可。为了方便使用set。
将每个栅栏插入平衡树,然后考虑栅栏间的影响,上面处理好了第一种情况。考虑另一种情况:
①A在B的左上方,而A比B先出现,那么A就对B有影响,能贡献到A的答案与B无关;
②A在B的左上方但是出现时间比B晚,那么A不会对B产生任何影响(这取决于点向栅栏贡献的方式),我们要在set中将所有这样的栅栏删掉,因为A已经贡献完了但是在B的左边影响B的答案。
后面就是点对栅栏的贡献方式了,经过上面的处理我们只需要向在点右上方的第一个栅栏贡献就好了。
最后我们要合并答案,就是当A在B的左下方第一个时,只有A有答案而B可能要加上A的。我们按时间倒序拿出所有栅栏,并将其答案加入并查集中,如果A在B的后面出现,就会将答案放进去了,而A的答案已经提前算好了。乱搞一通所有正确的答案就出来了。
时间复杂度O(nlogn)。
细节比较多,具体看代码吧。这真是一道神奇的题目啊。
代码
#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
int n, q;
struct Data{
int x, y, t;
bool operator < (const Data& OTHER) const{
if(y == OTHER.y) return x < OTHER.x;
return y > OTHER.y;
}
}p[maxn];
int par[maxn], cnt[maxn], Fa[maxn], ans[maxn];
int FindR(int x){
return (!Fa[x]) ? x : Fa[x] = FindR(Fa[x]);
}
void Merge(int x, int y){
x = FindR(x);
y = FindR(y);
if(x != y){
Fa[x] = y;
cnt[y] += cnt[x];
}
}
set <pair<int, int> > S;
int main(){
freopen("2439.in", "r", stdin);
freopen("2439.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d%d", &p[i].x, &p[i].y);
p[i].x = (p[i].x << 1) - 1;
p[i].y = (p[i].y << 1) - 1;
p[i].t = 0;
}
scanf("%d", &q);
for(int i = 1; i <= q; i++){
scanf("%d%d", &p[n+i].x, &p[n+i].y);
p[n+i].x <<= 1;
p[n+i].y <<= 1;
p[n+i].t = i;
}
sort(p+1, p+n+q+1);
for(int i = 1; i <= n+q; i++){
pair <int, int> tmp = make_pair(p[i].x, p[i].t);
if(p[i].t){
S.insert(tmp);
set <pair<int, int> >::iterator now = S.find(tmp);
if((++now) != S.end()) par[p[i].t] = now->second;
for(;;){
now = S.find(tmp);
if(now == S.begin()) break;
if((--now)->second < p[i].t) break;
S.erase(now);
}
}
else{
set <pair<int, int> >::iterator it = S.lower_bound(tmp);
if(it != S.end()) ++ cnt[it->second];
}
}
for(int i = q; i; i--){
ans[i] = cnt[FindR(i)];
if(par[i]) Merge(i, par[i]);
}
for(int i = 1; i <= q; i++) printf("%d\n", ans[i]);
return 0;
}