HDU5299 圆的扫描线 && 树上删边博弈

HDU5299 圆的扫描线 && 树上删边博弈

标签(空格分隔): 未分类


给出若干个圆,可以互相嵌套但不相交或相切。
每次删去一个圆和它内部的圆,进行博弈,问谁赢。


分成两部分。首先我们要处理出给定圆的嵌套关系,然后解决博弈问题。
首先保证圆不相交切不相切的话,所有圆之间的关系形成一堆树。
那么这个问题转化为树上每次删除一个边和它的子树,删不了为输,问谁赢。


树上删边问题:每个儿子的\(sg\)为1,\(一个节点的sg=所有儿子节点的\)(sg+1)\(的异或和\)


重点是怎么处理不相交圆的嵌套关系
这要用到圆的扫描线。
所有对不相交圆的的处理都可以用扫描线。
类似于hdu3511. hdu3511是让你求嵌套层数最深的一个圆,和这个题是一样的。

首先对于所有圆,一个圆创建两个事件点,一个进一个出。对所有时间点排序,按照x从小到大。
然后逐个处理事件点,用一个set维护所有圆。每当遇到一个进入事件,分四种情况讨论这个圆的位置关系,然后把这个圆插入set中。每当遇到一个离开事件,从set中删除这个圆。
set中圆的排序方法是按照当前扫描线所在的位置和圆的交点进行排序的。这里由于圆没有相交,所以交点的高低顺序不变,所以可以在\(opetator <\)中动态计算交点然后比较。
圆的四种情况:(引用自这里

  1. 没有上方事件点,或者没有下方事件点。这时该圆C是最外层的一个圆。
  2. 上方事件点和下方事件点属于同一个圆A,这时圆A必定是圆C的父亲。
  3. 上方事件点和下方事件点分别属于两个圆A,B,且fa(A)!=fa(B),如果fa(A)==B,那么A和C都是B的孩子。
  4. 上方事件点和下方事件点分别属于两个圆A,B,且fa(A)==fa(B),那么A和B都是C的兄弟。

注意为了方便最好在最外面套一个无限大的圆,避免第一种情况写出来出问题。

//扫描线
#define out (-1)
#define in (1)
#define UP (1)
#define DOWN (-1)
#define eps 1e-6
using namespace std;
int nowx;//扫描线的位置
struct circle{
    int x,y,r;
    circle(int x=0,int y=0,int r=0):x(x),y(y),r(r) {}
    void read(){cin>>x>>y>>r;}
}c[MAXN];
struct event{
    int x,type,ss;
    event(int x=0,int type=0,int ss=0):x(x),type(type),ss(ss) {}
}eve[MAXN];
bool operator < (const event&a,const event &b){
    if (a.x==b.x){
        if (a.type==b.type)        return a.ss<b.ss;
        else return a.type>b.type;
    }
    else return a.x<b.x;
}
struct node{
    int ss,type;
    node(int ss=0,int type=0):ss(ss),type(type) {}
};
double get_pos(const node &p){
    int type=p.type , ss=p.ss;
    if(type==UP)    return (double)c[ss].y+sqrt((double)c[ss].r*c[ss].r-(double)(c[ss].x-nowx)*(c[ss].x-nowx));
    if(type==DOWN)    return (double)c[ss].y-sqrt((double)c[ss].r*c[ss].r-(double)(c[ss].x-nowx)*(c[ss].x-nowx));
}
bool operator < (const node &a,const node &b){
    double A=get_pos(a),B=get_pos(b);
    return  A > B || fabs(A-B)<eps && a.type > b.type;
}
bool operator == (const node &a,const node &b){return a.ss==b.ss && a.type==b.type;}
set<node> s;
typedef set<node>::iterator it;
int fa[MAXN];
int main (int argc, char *argv[])
{
    int n;
    cin>>n;
    int cnt=1;
    FOR(i,1,n){
        c[i].read();
        eve[cnt++]=event(c[i].x-c[i].r,in,i);
        eve[cnt++]=event(c[i].x+c[i].r,out,i);
    }
    sort(eve+1,eve+cnt);
    FOR(i,1,cnt-1){
        int ss=eve[i].ss;
        nowx=eve[i].x;//移动扫描线到当前位置
        if (eve[i].type==in){
            it up=s.lower_bound(node(ss,UP));
            it down=s.upper_bound(node(ss,UP));
			//这里没有在最外层套一个大圆
            if (up==s.begin() || down==s.end()){//情况1
                fa[ss]=0;
                s.insert(node(ss,UP));
                s.insert(node(ss,DOWN));
                continue;
            }
            --up;
            if (up->ss==down->ss){//情况2
                fa[ss]=up->ss;
            }else if (fa[up->ss]!=fa[down->ss]){//情况3
                
                if (fa[up->ss]==down->ss)    fa[ss]=down->ss;
                else if (fa[down->ss]==up->ss)    fa[ss]=up->ss;
            }else{//情况4
                fa[ss]=fa[up->ss];
            }
            s.insert(node(ss,UP));
            s.insert(node(ss,DOWN));
        }else{
            s.erase(node(ss,UP));
            s.erase(node(ss,DOWN));
        }
    }

    return 0;
}

posted @ 2015-08-04 10:03  wsc500  阅读(661)  评论(0编辑  收藏  举报