【HDU5299】Circles Game-扫描线+set+树上删边博弈

测试地址:Circles Game
题目大意:给定n个圆,圆两两之间只可能有相离或包含两种关系。两个人博弈,每次可以取走一个圆以及被这个圆包含的所有圆,不能取的人输,问先手必胜还是必败。
做法:本题需要用到扫描线+set+树上删边博弈。
两个圆之间只可能相离或包含,那么显然这种包含关系能够构成一座森林的形状。如果再将所有不被任何圆包含的圆与一个空点相连,这就是一棵树了。意识到我们每次取走一个圆,在树中的体现就是切掉这个点的父边,把它的子树删掉。那么问题就变成不断删边,最后只剩下根节点时结束。这种博弈类型就是经典的树上删边博弈。
这种树上删边博弈是一个公平的组合游戏,因此可以用SG定理进行推导。我们首先发现,一条链的SG值肯定是这条链中的边数。如果树的形态是从根连出几条链的形式,这显然就是一个Nim游戏,所以这样的树的SG值为它的所有连出的链的边数的异或和。因为SG值相同,所以这种树也就等价于一条长度与其SG值对应的链。就这么一直推导下去,就可以O(n)推导出某一棵树的SG值了。这样我们就可以判断先手必胜还是必败了。
那么问题是不是就解决了呢?还没有。注意到一开始连边的过程中,我们需要找到包含一个圆的最小的圆,如果暴力找的话,时间复杂度最坏是O(n2)的,无法接受(但这道题好像数据出水了可以过)。所以我们需要运用另一种方法:扫描线。
一条垂直于x轴的扫描线从左往右扫描,因为圆与圆之间不会相交,所以圆与扫描线的交点的高低关系是不变的,因此我们用set维护圆与扫描线的交点。将一个圆拆分为两个事件:加入事件和删除事件。在加入一个圆时,我们需要求出包含它的最小圆的编号。在加入圆时,它与扫描线相切,于是我们在set里查找离它最近的两个交点,然后分类讨论:
1.如果上下有某一个方向找不到交点,表示当前圆在最外层。
2.如果上下交点属于同一个圆,则这个圆就是所求的最小包含圆。
3.如果上下交点不属于同一个圆,但属于两个同层的圆(层定义为从内到外最少需要经过的圆数,其实也就是在树中从根到该点的距离,可以在算法中顺便求出),那么这两个圆和当前圆被一个圆共同包含,这个圆就是这两个圆的父亲,它就是所求的最小包含圆。
4.如果上下交点属于两个不同层的圆,那么层数较大的圆和当前圆共同包含于层数较小的圆,则这个层数较小的圆为所求的最小包含圆。
于是我们就讨论完了所有的情况,这样就可以O(nlogn)完成连边的操作了。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,f[20010],F[20010],fa[20010],first[20010]={0},tot=0;
ll x[20010],y[20010],r[20010],now=0;
struct event
{
    bool type;
    int id,val;
}op[50010];
struct edge
{
    int v,next;
}e[20010];

double calc(int id,bool type)
{
    double R=(double)r[id],A=fabs((double)(x[id]-now)),Y=(double)y[id];
    if (!type) return Y-sqrt(R*R-A*A);
    else return Y+sqrt(R*R-A*A);
}

struct point
{
    int id;
    bool type;
    bool operator < (point a) const
    {
        if (id==a.id) return type<a.type;
        double ans1=calc(id,type),ans2=calc(a.id,a.type);
        return ans1<ans2;
    }
};
set<point> S;
set<point>::iterator it;

ll dis(ll x1,ll y1,ll x2,ll y2)
{
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

bool cmp(event a,event b)
{
    if (a.val==b.val) return a.type>b.type;
    else return a.val<b.val;
}

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs(int v)
{
    F[v]=0;
    for(int i=first[v];i;i=e[i].next)
    {
        dfs(e[i].v);
        F[v]^=(F[e[i].v]+1);
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld",&x[i],&y[i],&r[i]);
            int a=(i<<1)-1,b=(i<<1);
            op[a].id=i,op[a].val=x[i]-r[i],op[a].type=0;
            op[b].id=i,op[b].val=x[i]+r[i],op[b].type=1;
        }
        sort(op+1,op+(n<<1)+1,cmp);

        point nxt,up,down;
        x[0]=y[0]=0;r[0]=100000;
        S.clear();
        nxt.id=0,nxt.type=0;S.insert(nxt);
        nxt.id=0,nxt.type=1,S.insert(nxt);
        f[0]=-1;
        for(int i=1;i<=(n<<1);i++)
        {
            now=op[i].val;
            if (!op[i].type)
            {
                nxt.id=op[i].id,nxt.type=0;
                it=S.lower_bound(nxt);
                up=(*it);
                it--;
                down=(*it);
                if (up.id==down.id)
                {
                    f[op[i].id]=f[up.id]+1;
                    fa[op[i].id]=up.id;
                }
                else if (f[up.id]==f[down.id])
                {
                    f[op[i].id]=f[up.id];
                    fa[op[i].id]=fa[up.id];
                }
                else
                {
                    if (f[up.id]>f[down.id])
                        swap(up,down);
                    f[op[i].id]=f[up.id]+1;
                    fa[op[i].id]=up.id;
                }
                S.insert(nxt);
                nxt.type=1;
                S.insert(nxt);
            }
            else
            {
                nxt.id=op[i].id,nxt.type=0;
                S.erase(nxt);
                nxt.type=1;
                S.erase(nxt);
            }
        }

        memset(first,0,sizeof(first));
        tot=0;
        for(int i=1;i<=n;i++)
            insert(fa[i],i);

        dfs(0);
        printf("%s\n",F[0]?"Alice":"Bob");
    }

    return 0;
}
posted @ 2018-07-08 12:15  Maxwei_wzj  阅读(130)  评论(0编辑  收藏  举报