CodeChef - TELEPORT

题目链接: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] \times [y-2R,y], [x-2R,x] \times [y,y+2R], [x,x+2R] \times [y-2R, y], [x,2R] \times [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\n");
66             else    printf("NET\n");
67         }
68     }
69 }

 

     

posted @ 2018-01-25 00:22  Blogggggg  阅读(180)  评论(0编辑  收藏  举报