YZOJ P2050 [FJOI2013]圆形游戏
时间限制:8000MS 内存限制:262144KB
难度:\(8.0\)
-
题目描述
在一个无穷大的桌面上有 \(n\) 个圆形,保证任意两个圆相离或者相含,不存在相切或相交。现在 Alice 和 Bob 在玩一个圆形游戏,以 Alice 为先手,双方以如下步骤轮流游戏:
1,选定一个圆 \(A\),把 \(A\) 以及所有完全在 \(A\) 内部的圆都删除;
2,如果在自己回合无法找到可删除的圆,则输掉比赛。
假设 Alice 和 Bob 都非常聪明,请问最终谁能够取得胜利?请编程输出最终获胜的人。
-
输入格式
输入数据的第一行为一个正整数 \(T\),表示数据组数。
接下来 \(T\) 组数据,对于每组数据,第一行包含 \(1\) 个正整数 \(n\),表示圆形的个数。
之后 \(n\) 行,每行为 \(3\) 个整数 \(x\)、\(y\) 和 \(r\) ,分别表示圆形的圆心坐标以及圆的半径。
-
输出格式
假设 Alice 最后获胜,则输出一行 “Alice”(不包括引号),否则输出 “Bob” 。
-
样例输入
1 2 3 4 5 6 7 8 9 10 |
2 1 0 0 1 6 -100 0 90 -50 0 1 -20 0 1 100 0 90 47 0 1 23 0 1 |
-
样例输出
1 2 |
Alice Bob |
-
数据规模与约定
\(100\%\) 的数据满足 \(T \leq 100\),\(n \leq m20000\),\(\left|x\right|, \left|y\right|, r \leq 10^8\) 。
三·原题套在一起wwwww
首先把 \(n\) 个圆按照半径从小到大排序,那么对于第 \(i\) 个圆,在半径比它大的圆中找到第一个包含它的圆 \(j\) ,\(i \rightarrow\) 连边,那么此时会连成一个森林。
若再添加一个 \(x=0, y=0, r=\infty\) 的 \(0\) 号圆,那么此时会连成一棵以 \(0\) 为根节点的树。
然后就是一个 SG函数 啦,在上面 DP 一下,有 \(f_o = (f_{s_1}+1) \oplus (f_{s_2}+1) \oplus \cdots\) ,若 \(o\) 为叶子结点则 \(f_o=0\) 。(其实我也不知道为什么
这样转移是 \(O(n)\) 的,但是建树却是 \(O(n^2)\) 的,想办法优化。
其实可以使用扫描线维护这些圆的位置关系。
把圆拆成两个事件,\(x-r\) 插入且 \(x+r\) 弹出,并且把这个圆拆成上半圆和下半圆,近似看成右括号和左括号,那么其实就是要动态维护一个括号序列。
并且在 插入/弹出 前圆的位置关系都是不会发生改变的。
这样,例如 (())
就是包含关系,()()
就是相离关系。
要找出当前圆被哪一个圆完全包含,就是找当前左括号的前驱:若为左括号则被其包含,若为右括号则它们同时被另一个圆包含。
发现可以使用平衡树维护括号序列,时间复杂度 \(O(n \log n)\) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #include <algorithm> #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/tree_policy.hpp> #define _max(_a_,_b_) ((_a_)>(_b_)?(_a_):(_b_)) #ifdef ONLINE_JUDGE char __B[1<<15],*__S=__B,*__T=__B; #define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++) #endif inline int getnum() { register char c=0; register bool neg=false; while(!(c>='0' && c<='9')) c=getchar(),neg|=(c=='-'); register int a=0; while(c>='0' && c<='9') a=a*10+c-'0',c=getchar(); return (neg?-1:1)*a; } #pragma pack(1) struct _event { int x,id; bool ins; bool operator < (const _event&o)const { if(x != o.x) return x < o.x; return ins > o.ins; } }event[40505]; int le,x[20505],y[20505],r[20505]; int _curx; struct _node { int id; bool type; bool operator < (const _node&o)const { if(id == o.id) return type < o.type; #define _sqr(_x) ((long long)(_x)*(_x)) register long long p=_sqr(r[id])-_sqr(_curx-x[id]); register long long p1=_sqr(r[o.id])-_sqr(_curx-x[o.id]); return (type ? 1 : -1)*__builtin_sqrtl(p)+y[id] \ < (o.type ? 1 : -1)*__builtin_sqrtl(p1)+y[o.id]; } }; typedef __gnu_pbds::tree<_node,__gnu_pbds::null_type> _t; typedef _t::point_iterator _tit; _t tree; _tit itp[40505]; int father[20505]; int gcnt,ghead[20505],gnext[40505],gnode[40505]; inline void insertLine(register int s,register int t) { gnext[++gcnt]=ghead[s],ghead[s]=gcnt,gnode[gcnt]=t; gnext[++gcnt]=ghead[t],ghead[t]=gcnt,gnode[gcnt]=s; } int f[20505]; void DFS(register int o,register int fa) { f[o]=0; for(register int j=ghead[o],t;j;j=gnext[j]) if((t=gnode[j]) != fa) { DFS(t,o); f[o]^=(f[t]+1); } } int main() { register int _T=getnum(); while(_T--) { register int N=getnum(); le=0; for(register int i=1;i<=N;i++) { x[i]=getnum(),y[i]=getnum(),r[i]=getnum(); event[++le].x=x[i]-r[i],event[le].id=i,event[le].ins=true; event[++le].x=x[i]+r[i],event[le].id=i,event[le].ins=false; } std::sort(&event[1],&event[le+1]); tree.clear(),r[N+1]=INT_MAX>>1; tree.insert((_node){N+1,1}),tree.insert((_node){N+1,0}); _tit it; for(register int i=1;i<=le;i++) { _curx=event[i].x; if(event[i].ins) { tree.insert((_node){event[i].id,1}).first; it=tree.insert((_node){event[i].id,0}).first; if((--it)->type == 0) father[event[i].id]=it->id; else father[event[i].id]=father[it->id]; } else { tree.erase(tree.find((_node){event[i].id,1})); tree.erase(tree.find((_node){event[i].id,0})); } } gcnt=0,memset(ghead,0,sizeof(ghead)); for(register int i=1;i<=N;i++) insertLine(i,father[i]); DFS(N+1,0); printf("%s\n",(f[N+1] ? "Alice" : "Bob")); } return 0; } |