• 【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
    ",F[0]?"Alice":"Bob");
        }
    
        return 0;
    }
  • 相关阅读:
    linux下载和解压命令
    检测是否存在post类型的注入
    snort的学习
    解决ERROR: D:Snortetcsnort.conf(511) Unknown preprocessor: "sip". Fatal Error, Quitting..
    代理服务器
    Settimer及回调函数的用法
    vm安装ubantu的详细过程(转载)
    linux 常用指令汇总
    linux 用户及文件权限管理
    linux 基础知识
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793294.html
Copyright © 2020-2023  润新知