hdu 5299 Circles Game 2015 Multi-University Training Contest 1 计算几何+博弈SG函数 圆的扫描线

Circles Game

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 251    Accepted Submission(s): 51


Problem Description
There are n circles on a infinitely large table.With every two circle, either one contains another or isolates from the other.They are never crossed nor tangent.
Alice and Bob are playing a game concerning these circles.They take turn to play,Alice goes first:
1、Pick out a certain circle A,then delete A and every circle that is inside of A.
2、Failling to find a deletable circle within one round will lost the game.
Now,Alice and Bob are both smart guys,who will win the game,output the winner's name.
 

Input
The first line include a positive integer T<=20,indicating the total group number of the statistic.
As for the following T groups of statistic,the first line of every group must include a positive integer n to define the number of the circles.
And the following lines,each line consists of 3 integers x,y and r,stating the coordinate of the circle center and radius of the circle respectively.
n≤20000,|x|≤20000,|y|≤20000,r≤20000。
 

Output
If Alice won,output “Alice”,else output “Bob”
 

Sample Input
   
   
2 1 0 0 1 6 -100 0 90 -50 0 1 -20 0 1 100 0 90 47 0 1 23 0 1
 

Sample Output
   
   
Alice Bob
 

Author
FZUACM
 

Source


题意:有很多圆,相互包含或者相离,可以选着一个圆删掉,所有在该圆内的圆都会被删掉。Alice和Bob在玩游戏,Alice先手,问谁会赢。
方法:
       先预处理出每个圆与包含该圆的最小圆,建立一片森林。
                用对圆按半径排序,用线段树按x坐标排序,把x坐标相同的圆放到同一个结点中。处理一个圆的时候,用log的复杂度查到哪些圆满足x坐标满足X'- r,X'+r,然后枚举这些圆,看是否被该圆直接包含。然后建立一片森林。
       建立虚拟结点,把森林连成一棵树。
       此时游戏转化为每次删除树上一条边,并把所有无法到达根的点全部删除。最后不能删边的人输。
      树上的删边游戏,看论文《《OI2009-组合游戏略述——浅谈SG游戏的若干拓展及变形》》
     每个结点的sg函数  =  ^(孩子结点的sg+1)


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define maxn 200000
int lc[maxn],rc[maxn],size[maxn];
int cnt;
void init(){
    lc[0] = rc[0] = size[0] = 0;
    cnt = 1;
}
void build(int u,int l,int r){
    lc[u] = rc[u] = size[u] = 0;
    if(l == r) return ;
    lc[u] = cnt++;
    rc[u] = cnt++;
    build(lc[u],l,(l+r)/2);
    build(rc[u],(l+r)/2+1,r);
}
vector<int>head[maxn];

void add(int u,int l,int r,int p,int id){
    if(l == r){
        size[u]++;
        head[l].push_back(id);
        return ;
    }
    int mid = (l+r)/2;
    if(p > mid)
        add(rc[u],mid+1,r,p,id);
    else
        add(lc[u],l,mid,p,id);
    size[u]++;
}
struct Point{
    int x,y,r;
};
Point p[maxn];
int isin(Point a,Point b){
    int x = a.x-b.x;
    int y = a.y-b.y;
    if(x*x+y*y<=b.r*b.r) return 1;
    return 0;
}
vector<int> edge[maxn];
int in[maxn];
void query(int u,int l,int r,int id){
    if(size[u] < 0) return;
    if(l > p[id].x+p[id].r) return;
    if(r < p[id].x-p[id].r) return;
    if(l == r){
        for(int  i = 0;i < head[l].size();i++){
            int u = head[l][i];
            if(isin(p[u],p[id])){
                edge[id].push_back(u);
                in[u]++;
                head[l][i] = head[l][head[l].size()-1];
                head[l].pop_back();
                i--;
            }
        }
        return ;
    }
    int mid = (l+r)/2;
    query(lc[u],l,mid,id);
    query(rc[u],mid+1,r,id);
    size[u] = size[rc[u]]+size[lc[u]];
}
int comp(Point a,Point b){
    return a.r < b.r;
}

int dfs(int u){
    int ans = 0;
    for(int i = 0;i < edge[u].size();i++){
        ans ^= (dfs(edge[u][i])+1);
    }
    return ans;
}
int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 0;i < n; i++){
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].r);
            p[i].x += 20000;
            p[i].y += 20000;
        }
        init();
        cnt++;
        build(1,0,40000);
        sort(p,p+n,comp);
        memset(in,0,sizeof(in));
        for(int i = 0;i < maxn; i++){
            head[i].clear();
            edge[i].clear();
        }
        for(int i = 0;i < n; i++){
            query(1,0,40000,i);
              add(1,0,40000,p[i].x,i);
        }
        int ans = 0;
        for(int i = 0;i < n; i++){
            if(in[i] == 0){
                int u = dfs(i);
                ans ^=(u+1);
            }
        }
        if(ans == 0)
            cout<<"Bob"<<endl;
        else
            cout<<"Alice"<<endl;
    }
    return 0;
}

扫描线方法的代码如下:

详细看我的博客,介绍圆的扫描线http://blog.csdn.net/firenet1/article/details/47041145



#include<iostream>
#include<algorithm>
#include<cstdio>
#include<set>
#include<cstring>
#include<vector>
#include<math.h>
using namespace std;
#define maxn 100010
struct Circle{
    int id,x,r,y;
    Circle(int _id=0,int _x=0,int _y=0){
        id=_id, x=_x, y=_y;
    }
};
Circle cir[maxn];
//用于对圆的左右端点排序
Circle p[maxn];
int cmp(Circle a,Circle b){
    return a.x < b.x;
}
//用于扫面的位置排序,globalx是当前扫描线的位置
int globalx;

//计算圆id在x=globalx时的y坐标
double yposition(int id,int ty){
    double x = globalx - cir[id].x;
    double y = sqrt(cir[id].r*1.0*cir[id].r-x*x);
    if(ty == 1) return y+cir[id].y;
    return cir[id].y-y;
}
//记录扫描线的交点信息ty = 1表示上交点
struct Point{
    int id,ty;
    Point(int _id=0,int _ty=0){
        id = _id;
        ty = _ty;
    }
    //扫描线与圆的交点按y从大到小排序函数
    bool operator < (const Point & b)const{
        if(id == b.id)
            return ty > b.ty;
        return  yposition(id,ty) > yposition(b.id,b.ty);
    }
};

set<Point> haha;
vector<int>head[maxn];
int fa[maxn];
int dfs(int u){
    int ans = 0;
    for(int i = 0;i < head[u].size(); i++)
        ans ^= (dfs(head[u][i]) + 1);
    return ans;
}
int main(){
    int n,f,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 0;i < n; i++){
            scanf("%d%d%d",&cir[i].x,&cir[i].y,&cir[i].r);
            p[i*2]   = Circle(i,cir[i].x-cir[i].r,0);
            p[i*2+1] = Circle(i,cir[i].x+cir[i].r,1);
        }
        for(int i = 0;i <= n; i++)
            head[i].clear();
        int nn = 2 * n;
        sort(p,p+nn,cmp);
        haha.clear();
        set<Point>::iterator it1,it2;
        for(int i = 0;i < nn; i++){
            globalx = p[i].x;
            if(p[i].y == 1){
                haha.erase(Point(p[i].id,0));
                haha.erase(Point(p[i].id,1));
            }
            else {
                it1 = haha.insert(Point(p[i].id,1)).first;
                it2 = it1++;
                if(it1 == haha.end() || it2 == haha.begin())
                    f = n;
                else {
                    it2--;
                    if(it2->id == it1->id)
                        f = it1->id;
                    else if(fa[it1->id] == it2->id)
                        f =it2->id;
                    else if(fa[it2->id] == it1->id)
                        f = it1->id;
                    else
                        f = fa[it1->id];
                }
                haha.insert(Point(p[i].id,0));
                head[f].push_back(p[i].id);
                fa[p[i].id] = f;
            }
        }
        int ans = dfs(n);
        if(ans == 0)
            printf("Bob\n");
        else
            printf("Alice\n");
    }
    return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值