HDU 5809 Ants (KD_Tree + 并查集)

Ants

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 480    Accepted Submission(s): 130

 

Problem Description

Mr. Chopsticks found a lot of ants and ant nests on Mars. The behavior of these ants seems quite different from ants on earth, so Mr. Chopsticks built a laboratory to study them. In the laboratory, there is a flat land and N ant nests, numbered from 1 to N, on it. The ith nest’s location is (xi, yi), and no two nests are at the same location. After a period of observation, Mr. Chopsticks has found some laws which all the moving ants obey:

1.  When an ant is at a nest p, it always moves to another nest which is closest to p. If there are multiple nests with minimum distance from p, it moves to the one with smaller x-coordinate value. If there is still a tie, the one with smaller y-coordinate value is chosen. When an ant moves from a nest to another, it always moves along the segment connecting them.

2.  An ant never stops, that is, when an ant arrives at a nest, it moves to the next nest immediately. So, of course, an ant may visit a nest infinitely many times.

3.  All the ants move at the same speed.

What happens if two ants meet each other? Will they fight? Mr. Chopsticks is curious about these questions. But at first, he should know whether two ants will meet. So he considers the following question: Given two different nests, if two ants start to move from these two nests simultaneously, will they meet at some time during their moving?
Note that all the ants and nests can be considered as points.

 

 

Input

The input begins with an integer T (T <= 10), indicating the number of test cases. Each case begins with an integer N and Q (2 <= N <= 100000, 1 <= Q <= 100000), indicating the number of ant nests and the number of queries respectively. The following N lines, each contain two integers xi and yi (-1 000 000 000 <= xi, yi <= 1 000 000 000), indicating the location of the ith ant nest. The following Q lines each contain two integers i and j (1 <= i, j <= N, i ≠ j), indicating the number of the two given ant nests.

 

 

Output

For each case, output "Case #X:" in a line where X is the case number, staring from 1. Then for each query, output “YES” in a line if the two ants will meet; otherwise output “NO”.
 

 

 

Sample Input

 

2 2 1 0 0 -1 1 1 2 5 2 1 1 3 3 4 4 0 -3 0 -4 1 3 2 4

 

 

Sample Output

 

Case #1: YES Case #2: YES NO

 

 

Author

SYSU

 

 

Source

2016 Multi-University Training Contest 7

 

 

 

 

        大致题意:有很n个蚂蚁窝,蚂蚁窝里面的蚂蚁运动的时候有一个规律,就是每次往距离它最近的蚂蚁窝走。当两对蚂蚁窝距离相同时,坐标小的那个更近。而且蚂蚁运动严格是走直线,现在有q个询问,每个询问给出两个蚂蚁窝的编号,问这两个蚂蚁窝的蚂蚁是否能够相遇。

 

        首先,处理这个距离最近的蚂蚁窝,显然是用KD树解决。对于给出的二维坐标,建立KD树,对于每一个点求一次最近点。其次,是判断能否相遇,这个也很容易看出就是判断两个点是否在统一连通分量里面。这个用不着tarjan,直接用并查集维护即可。

 

        方法比较简单,但是实现的时候有几个坑点。第一,我们求最近的点不能是他自己,这个可以在求解的过程中判断两个点之间的最近距离不能是0。第二,是这个距离的求解,按照之前模板给出的,距离求解用到了宏定义的sqr(),但是宏定义是没有类型的声明,直接默认为int类型的,所以对于本题这个坐标会到1e9的情况,用宏定义回爆int。我当初就因为没有考虑到这个而浪费了几乎两个半小时。具体见代码:

#include<bits/stdc++.h>
#define N 100010
#define K 2

using namespace std;

int idx,n,q,k=2,f[N];

struct Node
{
    int x[K],id;
    bool operator < (const Node &u) const
    {
        for(int i=0;i<k;i++)
            if (x[i]!=u.x[i]) return x[i]<u.x[i];
        return 0;
    }
} P[N];

typedef pair<double,Node> PDN;
double sqr(double x) {return x*x;}
priority_queue<PDN> que;

bool cmp(const Node a,const Node b)
{
    return a.x[idx]<b.x[idx];
}

struct KD_Tree
{
    int sz[N<<2]; Node p[N<<2];

    void build(int i,int l,int r,int dep)
    {
        if (l>r) return;
        int mid=(l+r)>>1;
        idx=dep%k;sz[i]=r-l;
        sz[i<<1]=sz[i<<1|1]=-1;
        nth_element(P+l,P+mid,P+r+1,cmp);
        p[i]=P[mid];
        build(i<<1,l,mid-1,dep+1);
        build(i<<1|1,mid+1,r,dep+1);
    }

    void query(int i,int m,int dep,Node a)
    {
        if (sz[i]==-1) return;
        PDN tmp=PDN(0,p[i]);
        for(int j=0;j<k;j++)
            tmp.first+=sqr(tmp.second.x[j]-a.x[j]);
        int lc=i<<1,rc=i<<1|1,dim=dep%k,flag=0;
        if (a.x[dim]>=p[i].x[dim]) swap(lc,rc);
        if (~sz[lc]) query(lc,m,dep+1,a);
        if (tmp.first&&que.size()<m) que.push(tmp),flag=1;
        else
        {
            if (tmp.first&&tmp<que.top()) que.pop(),que.push(tmp);
            if (sqr(a.x[dim]-p[i].x[dim])<=que.top().first) flag=1;
        }
        if (~sz[rc]&&flag) query(rc,m,dep+1,a);
    }

} KDT;

int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}

int main()
{
    int T_T,T=0;
    scanf("%d",&T_T);
    while(T_T--)
    {
        scanf("%d%d",&n,&q);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&P[i].x[0],&P[i].x[1]);
            P[i].id=f[i]=i;
        }
        KDT.build(1,0,n-1,0);
        for(int i=0;i<n;i++)
        {
            que.push(PDN(1e19,P[i]));
            KDT.query(1,1,0,P[i]);
            Node tmp=que.top().second;
            int u=find(P[i].id);
            int v=find(tmp.id);
            f[u]=v;
            while(!que.empty()) que.pop();
        }
        printf("Case #%d:\n",++T);
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y); x--; y--;
            puts(find(x)==find(y)?"YES":"NO");
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值