障碍点数和询问点数都是1e5 坐标范围为1e9(实际数据既然有大于1e9的)
一个点的下方或左边存在必败点,则为必胜点,否则为必败点
同一行的障碍会把这一行分成很多段,段与段之间是互不影响的。
考虑同一段的点,若其中一个点为必败点,则之后的点一定是必胜点,也就是说要找到第一个必败点
扫描线枚举每一行,用线段树维护每个位置是否有必败点,每一段的询问相当于在线段树上二分找到第一个没有必败点的位置
但是有很多行,直接枚举会T
一个空行会把第一个没有必败点的位置变成有必败点
如果有
x
x
行连续的空行,则就是把前 个没有必败点的位置变成有必败点
也在线段树上搞就好了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <cstring>
#include <vector>
#define ALL(x) (x).begin(),(x).end()
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
const int N=600010,MAX=1e9+5e7;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(); x=0;
for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}
int t,n,q;
vector<int> sb,za[N];
vector<pii> qy[N];
pii b[N],Q[N];
int tag[N*50],tot[N*50],ls[N*50],rs[N*50],cnt,rt,ans[N],ll[N*50],rr[N*50];
void cl(int &g,int L,int R){
if(!g) g=++cnt,ll[g]=L,rr[g]=R;
tag[g]=1; tot[g]=rr[g]-ll[g]+1;
}
void Push(int g){
if(tag[g]){
int mid=ll[g]+rr[g]>>1;
cl(ls[g],ll[g],mid); cl(rs[g],mid+1,rr[g]); tag[g]=0;
}
}
void Up(int g){
tag[g]=tag[ls[g]]&tag[rs[g]];
tot[g]=tot[ls[g]]+tot[rs[g]];
}
void set0(int &g,int x,int L,int R){
if(!g) g=++cnt,ll[g]=L,rr[g]=R;
if(L==R) return tag[g]=tot[g]=0,void();
int mid=L+R>>1; Push(g);
if(x<=mid) set0(ls[g],x,L,mid); else set0(rs[g],x,mid+1,R);
Up(g);
}
void set1(int &g,int l,int r,int L,int R){
if(!g) g=++cnt,ll[g]=L,rr[g]=R;
if(tag[g]) return ;
if(l==L && r==R) return cl(g,L,R);
int mid=L+R>>1;
if(r<=mid) set1(ls[g],l,r,L,mid);
else if(l>mid) set1(rs[g],l,r,mid+1,R);
else set1(ls[g],l,mid,L,mid),set1(rs[g],mid+1,r,mid+1,R);
Up(g);
}
int Query(int g,int l,int r,int L,int R){
if(tag[g]) return -1;
if(L==R) return l;
int mid=L+R>>1;
if(l==L && r==R){
if(!tag[ls[g]]) return Query(ls[g],l,mid,L,mid);
else return Query(rs[g],mid+1,r,mid+1,R);
}
if(r<=mid) return Query(ls[g],l,r,L,mid);
else if(l>mid) return Query(rs[g],l,r,mid+1,R);
int ret=Query(ls[g],l,mid,L,mid);
if(~ret) return ret;
return Query(rs[g],mid+1,r,mid+1,R);
}
int Find(int x){
int L=0,R=MAX,cur=rt;
while(L<=R){
if(L==R) return tot[cur]?-1:L;
int mid=L+R>>1;
if(mid-L+1-tot[ls[cur]]<x)
x-=mid-L+1-tot[ls[cur]],cur=rs[cur],L=mid+1;
else
cur=ls[cur],R=mid;
}
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
read(t);
while(t--){
read(n);
sb.push_back(0);
for(int i=1;i<=n;i++){
read(b[i].fi),read(b[i].se);
sb.push_back(b[i].fi);
}
read(q);
for(int i=1;i<=q;i++)
read(Q[i].fi),read(Q[i].se),sb.push_back(Q[i].fi);
sort(ALL(sb)); sb.erase(unique(ALL(sb)),sb.end());
for(int i=1;i<=n;i++){
b[i].fi=lower_bound(ALL(sb),b[i].fi)-sb.begin();
za[b[i].fi].push_back(b[i].se);
}
for(int i=1;i<=q;i++){
Q[i].fi=lower_bound(ALL(sb),Q[i].fi)-sb.begin();
qy[Q[i].fi].push_back(pii(Q[i].se,i));
}
for(int x=0;x<sb.size();x++){
za[x].push_back(-1); za[x].push_back(MAX+1);
sort(ALL(za[x])); sort(ALL(qy[x]));
vector<int> fail;
for(int i=0;i<za[x].size()-1;i++)
if(za[x][i]+1<za[x][i+1]){
int pos=Query(rt,za[x][i]+1,za[x][i+1]-1,0,MAX);
if(~pos){
fail.push_back(pos),set1(rt,pos,pos,0,MAX);
//for(int j=0;j<fail.size();j++)
// printf("%d %d\n",sb[i],fail[j]);
}
}
for(int i=0;i<qy[x].size();i++){
if(!fail.size() || qy[x][i].fi>*fail.rbegin())
ans[qy[x][i].se]=1;
else
ans[qy[x][i].se]=*lower_bound(ALL(fail),qy[x][i].fi)!=qy[x][i].fi;
}
for(int i=1;i<za[x].size()-1;i++) set0(rt,za[x][i],0,MAX);
if(x+1<sb.size()){
int pos=Find(sb[x+1]-sb[x]-1);
if(~pos) set1(rt,0,pos,0,MAX);
}
}
for(int i=1;i<=q;i++){
puts(ans[i]?"Alice":"Bob"),ans[i]=0;
}
for(int i=0;i<sb.size();i++) za[i].clear(),qy[i].clear();
for(int i=1;i<=cnt;i++) ls[i]=rs[i]=tag[i]=0; cnt=rt=0;
sb.clear();
}
return 0;
}