zoukankan      html  css  js  c++  java
  • 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 }

         

    “这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
  • 相关阅读:
    线程安全
    Thread 的join方法
    守护线程和用户线程
    LinkedList封装
    System.arraycopy的测试
    ArrayList封装
    常用算法
    Java并发---concurrent包
    Java并发--三大性质
    Java并发--final关键字
  • 原文地址:https://www.cnblogs.com/Blogggggg/p/8344089.html
Copyright © 2011-2022 走看看