首页 > 代码库 > [北京集训测试赛(五)/HDU5299]圆圈游戏(Circles game)-树上删边-圆的扫描线

[北京集训测试赛(五)/HDU5299]圆圈游戏(Circles game)-树上删边-圆的扫描线

Problem 遗产

题目大意

一个平面上n个圆,任两个圆只会相离或包含,给出每个圆位置与半径。

alice&&bob轮流取圆,每取一个就可以取出这个圆以及被这个圆包含的圆。

没圆取的人输,alice先取,问谁有必胜策略。

Solution

Method #1

首先我们考虑一个暴力一点的写法:

先将圆半径从小到大排序,然后枚举两个圆,判断是否包含关系。

如果包含的话就将大圆连一条边到小圆。

很容易发现,这样执行完以后得到了一棵树。

我们接下来要做的事情就变成了:

每个人可以轮流从树上删除一条边,所有与根节点不相连的点全都被删除。

谁不能取谁就输。

预备知识:树上删边

叶子节点值为0;

除叶子节点外任意节点值为其所有儿子的值加一的异或和。

根节点值为0则后取者胜。

dfs一遍树即为答案。

 

接下来很高兴啊!这题A掉啦!

但是——这smg?

$O(n^2)$的算法a掉了20000??还是700ms??

hdu的数据怎么不上天?

自己的oj交一发,妥妥的30pts tle。

 

Method #2

好了我们来换思路吧。

首先还是对半径进行排序。

 

我们对于每一个圆,从左到右扫描这个圆的范围。

如果在这个范围内找到了点,那么就判断是否包含,若包含则连边。

大致方法与上面一样。

我们对于每一个x坐标开一个vector记录该坐标线上的点。

 

提交hdu,ac100ms。

自己oj,90pts,还是被卡掉了。

仔细思考扫了一下,构造数据的确是可以卡掉这个算法的。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 using namespace std;
 7 vector<int> f[80010];
 8 int n,T,h[80010],tot=0,t;
 9 //bool cheat;
10 struct Circle{
11     int x,y,r;
12     bool inc;
13 }c[80010];
14 struct node{
15     int next,to;
16 }a[80010];
17 bool cmp(Circle a,Circle b){
18     return a.r<b.r;
19 }
20 void add(int u,int v){
21     a[++tot].to=v;
22     a[tot].next=h[u];
23     h[u]=tot;
24 }
25 int dfs_sg(int x,int fa){
26     int ret=0;
27     for(int i=h[x];~i;i=a[i].next)
28         if(a[i].to!=fa)ret^=(dfs_sg(a[i].to,x)+1);
29     return ret;
30 }
31 int main(){
32 //  freopen("08b.in","r",stdin);
33     scanf("%d",&T);
34     while(T--){
35         tot=0,cheat=1;
36         memset(a,0,sizeof(a));
37         memset(c,0,sizeof(c));
38         memset(h,-1,sizeof(h));
39         for(int i=0;i<=80001;i++)f[i].clear();
40         scanf("%d",&n);
41         for(int i=1;i<=n;i++)
42             scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r),
43             c[i].x+=40000,c[i].y+=40000;
44 //      for(int i=1;i<=100;i++)
45 //          if(c[i].r!=1){
46 //              cheat=0;
47 //              break;
48 //          }
49 //      if(cheat){
50 //          printf("Bob\n");
51 //          continue;
52 //      }
53         sort(c+1,c+1+n,cmp);
54         for(int i=1;i<=n;i++){
55             for(int p=c[i].x-c[i].r+1;p<=c[i].x+c[i].r-1;p++){
56                 t=0;
57                 while(t<f[p].size()){
58                     if((c[f[p][t]].x-c[i].x)*
59                       (c[f[p][t]].x-c[i].x)+
60                       (c[f[p][t]].y-c[i].y)*
61                       (c[f[p][t]].y-c[i].y)<=
62                       (long long)((c[i].r)*(c[i].r))){
63                         c[f[p][t]].inc=1;
64                         add(i,f[p][t]);
65                         f[p][t]=f[p][f[p].size()-1];
66                         f[p].pop_back();
67                         t--;
68                     }
69                     t++;
70                 }
71             }
72             f[c[i].x].push_back(i);
73         }
74         int ans=0;
75         for(int i=1;i<=n;i++)
76             if(!c[i].inc)ans^=(dfs_sg(i,-1)+1);
77         if(!ans)printf("Bob\n");
78         else printf("Alice\n");
79     }
80 }

 

Method #3

我们将每个圆的左端点&&右端点出现的时间排序。

对于每一个圆的端点来说,左端点sign为+1,表示该圆开始出现,右端点sign为-1,表示该圆结束。

我们开一个set来维护这些圆与扫描线的交点。set的键值为交点的y坐标值,如下计算:

$c_i\cap(x=now)={c_i.y\pm \sqrt{(c_i.r)^2-(c_i.x-now)^2}} $

只要给set开一个operater就可以了。

对于圆$c_i$的起始交点,我们get到它的upper bound

upperbound交点有三种可能,

一种是没有该交点,这说明$c_i$一定是不被其他圆包含的圆。

一种是该交点所属的圆$c_j$包含$c_i$,这说明$c_i$的父亲是圆$c_j$,而且不会有更近的父亲。

还有是圆$c_j$不包含圆$c_i$。这说明$c_i$圆的父亲是圆$c_j$的父亲的儿子。

第三种可能我们需要特判一下,如果圆$c_j$的父亲为0,那么不就不用连边。否则会爆空间。

判断sign为正的时候insert,sign为负的时候erase即可。

建树完毕后dfs即可。

 

hdu 78ms,

oj终于a掉了。

强烈吐槽hdu的数据。

最垃圾的暴力都能a掉,出数据的人可能比较强吧不屑于出这种题目的数据。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <set>
 7 using namespace std;
 8 int n,h[80010],fa[80010],tot=0,now,T,ndtot=0;
 9 struct Node{
10     int next,to;
11 }a[80010];
12 struct Circle{
13     int x,y,r;
14 }c[80010];
15 struct Time{
16     int x,id;
17     friend inline bool operator <(Time x,Time y){
18         return x.x<y.x;
19     }
20 }t[80010];
21 struct node{
22     int sign,id;
23     inline double y(){
24         return c[id].y+sign*sqrt((c[id].r)*(c[id].r)
25         -(now-c[id].x)*(now-c[id].x));
26     }
27     friend inline bool operator <(node x,node y){
28         return x.id==y.id?x.sign<y.sign:x.y()<y.y();
29     }
30 };
31 int dfs_sg(int x,int fa){
32     int ret=0;
33     for(int i=h[x];~i;i=a[i].next)
34         if(a[i].to!=fa)ret^=(dfs_sg(a[i].to,x)+1);
35     return ret;
36 }
37 void add(int u,int v){
38     a[++ndtot].to=v;
39     a[ndtot].next=h[u];
40     h[u]=ndtot;
41 }
42 set<node> S;
43 int main(){
44 //  freopen("08b.in","r",stdin);
45     scanf("%d",&T);
46     while(T--){
47         memset(a,0,sizeof(a));
48         memset(c,0,sizeof(c));
49         memset(t,0,sizeof(t));
50         memset(h,-1,sizeof(h));
51         memset(fa,0,sizeof(fa));
52         S.clear();tot=0;ndtot=0;
53         scanf("%d",&n);
54         for(int i=1;i<=n;i++)scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r);
55         for(int i=1;i<=n;i++)
56             t[++tot]=(Time){c[i].x-c[i].r,i},
57             t[++tot]=(Time){c[i].x+c[i].r,-i};
58         sort(t+1,t+tot+1);
59         for(int i=1;i<=tot;i++){
60             now=t[i].x;
61             if(t[i].id>0){
62                 set<node>::iterator it=S.upper_bound((node){+1,t[i].id});
63                 if(it==S.end()) fa[t[i].id]=0;
64                 else if(it->sign==1) add(it->id,t[i].id),fa[t[i].id]=it->id;
65                 else{
66                     if(fa[it->id])add(fa[it->id],t[i].id);
67                     fa[t[i].id]=fa[it->id];
68                 }        
69                 S.insert((node){-1,t[i].id}),S.insert((node){+1,t[i].id});
70             }
71             else S.erase((node){-1,-t[i].id}),S.erase((node){+1,-t[i].id});
72         }
73         int ans=0;
74         for(int i=1;i<=n;i++)
75             if(!fa[i])ans^=(dfs_sg(i,-1)+1);
76         if(!ans)printf("Bob\n");
77         else printf("Alice\n");
78     }
79     return 0;
80 }

 

[北京集训测试赛(五)/HDU5299]圆圈游戏(Circles game)-树上删边-圆的扫描线