CodeChef

题目链接: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 }

     

“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/8344089.html