HDU 5809 Ants (KDtree+Tarjan)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5809


题解:

KDtree的板题啊,求出每一个点的最近的那个点,然后建立双向边,然后用Tarjan进行缩点,对于每一次查询,判断两个点是否在同一个联通块里就行了。就是代码长了点?没人愿意补这个题吧。。。


#include<bits/stdc++.h>
#define mp Point
using namespace std;
typedef long long ll;
const ll inf=9e18;
const int MAXN=1e5+5;
namespace KD_Tree
{
    struct node
    {
        node *ch[2];
        ll d[2],mx[2],my[2];
        int id;//d表示这个点的坐标[0]->x,[1]->y,mx表示这个平面的x的范围,my表示这个平面的y的范围
        inline void push_up()
        {
            for(int i=0;i<=1;i++)
            {
                if(ch[i])
                {
                    mx[0]=min(mx[0],ch[i]->mx[0]);
                    mx[1]=max(mx[1],ch[i]->mx[1]);
                    my[0]=min(my[0],ch[i]->my[0]);
                    my[1]=max(my[1],ch[i]->my[1]);
                }
            }
        }
    }pool_node[MAXN],*pool_top=pool_node;
    node *del_pool[MAXN],**del_top=del_pool;
    inline node* newnode()
    {
        return del_top==del_pool?++pool_top:*(del_top--);
    }
    struct Point
    {
        ll x,y;
        int id;
        bool operator < (const Point &a)const
        {
            if(x==a.x)
                return y<a.y;
            return x<a.x;
        }
        Point(){}
        Point(ll _x,ll _y,int _id):x(_x),y(_y),id(_id){}
    };
    inline bool cmp_x(const Point &a,const Point &b) {return a.x<b.x;}
    inline bool cmp_y(const Point &a,const Point &b) {return a.y<b.y;}
    node **rebuild_need;
    int rebuild_d;
    Point stk[MAXN];
    int point_cnt;
    //用于最开始建树,可以不用直接插点,方法为build(1,point_cnt,0),保证点在stk数组中
    node* build(int l,int r,bool f)
    {
        int mid=(l+r)>>1;
        node *o=newnode();
        nth_element(stk+l,stk+mid,stk+r+1,!f?cmp_x:cmp_y);
        o->d[0]=o->mx[0]=o->mx[1]=stk[mid].x;
        o->d[1]=o->my[0]=o->my[1]=stk[mid].y;
        o->id=stk[mid].id;
        o->ch[0]=l<mid?build(l,mid-1,f^1):0;
        o->ch[1]=mid<r?build(mid+1,r,f^1):0;
        o->push_up();
        return o;
    }
    Point P,book;
    ll ans;
    ll calc_mn(node *o)//Manhattan距离下进行计算,如果是欧氏距离,则为max(P.x-o->mx[1],0)+max(o->mx[0]-P.x,0)的平方(本处表示到这个子树平面的最短距离)
    {
        ll ret=0;
        ret+=(max(P.x-o->mx[1],0LL)+max(o->mx[0]-P.x,0LL))*(max(P.x-o->mx[1],0LL)+max(o->mx[0]-P.x,0LL));
        ret+=(max(P.y-o->my[1],0LL)+max(o->my[0]-P.y,0LL))*(max(P.y-o->my[1],0LL)+max(o->my[0]-P.y,0LL));
        return ret;
    }
    void Query_Min(node *o)//查询平面中到目标点最近的点
    {
        if(o->id!=P.id)
        {
            ll dis=((o->d[0]-P.x)*(o->d[0]-P.x)+(o->d[1]-P.y)*(o->d[1]-P.y));
            if(dis==ans)
            {
                if(mp(o->d[0],o->d[1],o->id)<book)
                {
                    book=mp(o->d[0],o->d[1],o->id);
                }
            }
            else if(dis<ans)
            {
                book=mp(o->d[0],o->d[1],o->id);
                ans=dis;
            }
        }
        ll dl=o->ch[0]?calc_mn(o->ch[0]):inf;
        ll dr=o->ch[1]?calc_mn(o->ch[1]):inf;
        if(dl<dr)
        {
            if(dl<=ans)
                Query_Min(o->ch[0]);
            if(dr<=ans)
                Query_Min(o->ch[1]);
        }
        else
        {
            if(dr<=ans)
                Query_Min(o->ch[1]);
            if(dl<=ans)
                Query_Min(o->ch[0]);
        }
    }
    node *root;
    int Query(ll x,ll y,int id)
    {
        P=Point(x,y,id);
        ans=inf;
        Query_Min(root);
        return book.id;
    }
    void del(node *&a)//用于清空整颗树
    {
        for(int i=0;i<=1;i++)
        {
            if(a->ch[i])
            {
                del(a->ch[i]);
            }
        }
        a=NULL;
    }
    void init()//用于初始化
    {
        del_top=del_pool,pool_top=pool_node;
    }
}
struct p
{
    ll x,y;
}sv[MAXN];
struct Edge
{
    int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~scc
int Index,top;
int scc;//强连通分量的个数
bool Instack[MAXN]; 
//num数组不一定需要,结合实际情况
void addedge(int u,int v)
{
    edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
}
void fi(int now)
{
    int to=KD_Tree :: Query(sv[now].x,sv[now].y,now);
    addedge(now,to);
    addedge(to,now);
}
void Tarjan(int u)
{
    int v;
    Low[u]=DFN[u]=++Index;
    Stack[top++]=u;
    Instack[u]=true;
    for(int i = head[u];i != -1;i = edge[i].next)
    {
        v=edge[i].to;
        if(!DFN[v])
        {
            Tarjan(v);
            if(Low[u]>Low[v])Low[u]=Low[v];
        }
        else if(Instack[v]&&Low[u]>DFN[v])
            Low[u]=DFN[v];
    }
    if(Low[u]==DFN[u])
    {
        scc++;
        do
        {
            v=Stack[--top];
            Instack[v] = false;
            Belong[v] = scc;
        }
        while(v!=u);
    }
}
void solve(int N)
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    Index=scc=top=0;
    for(int i=1;i<=N;i++)
        if(!DFN[i])
            Tarjan(i);
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    scanf("%d",&T);
    for(int _=1;_<=T;_++)
    {
        int n,q;
        KD_Tree :: init();
        init();
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld",&KD_Tree :: stk[i].x,&KD_Tree :: stk[i].y);
            KD_Tree :: stk[i].id=i;
            sv[i].x=KD_Tree :: stk[i].x;
            sv[i].y=KD_Tree :: stk[i].y;
        }
        KD_Tree :: root=KD_Tree :: build(1,n,0);
        for(int i=1;i<=n;i++)
        {
            fi(i);
        }
        solve(n);
        printf("Case #%d:\n",_);
        for(int i=1;i<=q;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(Belong[x]==Belong[y])
                printf("YES\n");
            else
                printf("NO\n");
        }
        KD_Tree :: del(KD_Tree :: root);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值