题目链接:https://vjudge.net/problem/CodeChef-TELEPORT
题目大意:
有(Q)个指令,指令为:(+) (x) (y)(在二维平面内添加一个点,坐标为((x,y)));或(?) (i) (j)(如果第(i)个指令所添加的点和第(j)个指令所添加的点联通,则打印 "DA",否则打印 “NET").
关于联通的定义:如果每个点能到达的区域为({(a,b):|a-x|+|b-y| le R}).如果点(u)和点(v)联通,点(v)和点(k)联通,则点(u)和点(k)联通。
(1 le Q,R,x,y le 100,000)
知识点: 线段树、并查集
解题思路:
易知每个点能到达的区域为一个对角线长度为(2R)的正菱形,我们可以通过将其旋转(45^circ )来将其转换成正方形,具体的转化方法为
(x' = x + y)
(y' = x - y)
而该正方形的连通区域也转变成了({(a',b'):|a'-x'|+|b'-y'| le R}).
具体证明:(参考(chao)自官方题解)
(|x-a|+|y-b| = max(x-a+y-b, x-a+b-y, a-x+y-b, a-x+b-y) = max(|(x+y)-(a+b)|, |(x-y)-(a-b)|) = max(|x'-a'|, |y'-b'|) le R)
(转化的部分才是本题的精髓所在)
转化为正方形之后,对于坐标轴的 (x) 轴建立线段树,用(set)记录(x)在该区间中的中心点所对应(y)值及其指令编号。
添加的时候先查询一下在
([x-2R,x] imes [y-2R,y], [x-2R,x] imes [y,y+2R], [x,x+2R] imes [y-2R, y], [x,2R] imes [y,y+2R] )
这(4)个正方形区域中有没有点,如果有,则将各个区域中最靠近的点和要添加的点用并查集合并在一起。
询问的时候只需询问两个点是否并在一起了即可。
具体实现看代码及注释
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define lson l,m,rt<<1 4 #define rson m+1,r,rt<<1|1 5 6 const int maxn=500000,RR=200000; 7 typedef pair<int,int> P; 8 set<P> tree[maxn<<2]; //(y值,第几次查询) 9 void update(int pos,int y,int q,int l,int r,int rt){//在pos点插入纵坐标为y,查询编号为q的点 10 tree[rt].insert(make_pair(y,q)); 11 if(l==r) return; 12 int m=(l+r)>>1; 13 if(pos<=m) update(pos,y,q,lson); 14 else update(pos,y,q,rson); 15 } 16 int query(int L,int R,int y,int aR,int l,int r,int rt){//查询[L,R]*[y,2*aR]的正方形区域 17 if(L<=l&&r<=R){ 18 set<P>::iterator it=tree[rt].lower_bound(make_pair(y,0)); //找出纵坐标大于或等于y的最小值 19 if(it != tree[rt].end() &&(it->first <= y+2*aR)) return it->second; //返回对应的编号(从1开始) 20 return 0;//否则返回0 21 } 22 int m=(l+r)>>1; 23 int ret=0; 24 if(L<=m) ret=max(ret,query(L,min(m,R),y,aR,lson)); 25 if(R>m) ret=max(ret,query(max(L,m+1),R,y,aR,rson)); 26 return ret; 27 } 28 29 //并查集部分 30 int par[maxn]; 31 void init(int n){ 32 for(int i=0;i<=n;i++) par[i]=i; 33 } 34 int finds(int x){ 35 if(par[x]==x) return x; 36 return par[x]=finds(par[x]); 37 } 38 void unite(int x,int y){ 39 int tx=finds(x); 40 int ty=finds(y); 41 if(tx==ty) return; 42 par[tx]=ty; 43 } 44 //******************************************************* 45 int main(){ 46 int Q,R,x,y; 47 char tmp[5]; 48 scanf("%d%d",&Q,&R); 49 init(Q); 50 for(int q=1;q<=Q;q++){ 51 scanf("%s %d %d",tmp,&x,&y); 52 int tx=x+y+2*R,ty=x-y; //因为后面会查询到[tx-2*R,tx]这个区间,所以我们统一先将其加上2*R,避免出现负数,数组也要相应地开大一点 53 if(tmp[0]=='+'){ 54 int x1=query(tx-2*R,tx,ty-2*R,R,1,maxn,1); 55 int x2=query(tx-2*R,tx,ty,R,1,maxn,1); 56 int x3=query(tx,tx+2*R,ty-2*R,R,1,maxn,1); 57 int x4=query(tx,tx+2*R,ty,R,1,maxn,1); 58 if(x1) unite(x1,q); 59 if(x2) unite(x2,q); 60 if(x3) unite(x3,q); 61 if(x4) unite(x4,q); 62 update(tx,ty,q,1,maxn,1); 63 } 64 else{ 65 if(finds(x)==finds(y)) printf("DA "); 66 else printf("NET "); 67 } 68 } 69 }