[北京集训测试赛(五)/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 }
时间: 2024-10-07 06:30:50

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

[2016北京集训测试赛17]crash的游戏-[组合数+斯特林数+拉格朗日插值]

Description Solution 核心思想是把组合数当成一个奇怪的多项式,然后拉格朗日插值..:哦对了,还要用到第二类斯特林数(就是把若干个球放到若干个盒子)的一个公式: $x^{n}=\sum _{i=0}^{n}C(n,i)*i!*S(i,x)$ 围观大佬博客(qaq公式太难打了) Code #include<iostream> #include<cstdio> #include<cstring> #include<cmath> using na

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

[2016北京集训测试赛(五)]打地鼠-[思考题]

Description Solution 我们先只考虑一只地鼠的情况,依题意得,在某一个时刻该地鼠的可能停留位置是一个公差为2的等差数列.我们设这个等差数列的两端为[L,R].则如果区间[L+1,R-1]的格子被打实际上是不会影响L和R的(列一个等差数列实际模拟一下就发现啦).而如果格子L被打,则L+2:如果格子R被打,则R-2.打了格子后,别忘了L--,R++. 嗯根据以上性质,我们可以知道,地鼠1,3,5,7,9...的L是非递减的,地鼠2,4,6,8,10...的L也是非递减的. 然后看一

[北京集训测试赛(三)]灯(Light)-奇怪乱搞数学题-素数

Problem 灯 题目大意 n盏灯排成一列,标号一到n,一开始标号为1的灯亮着. 现在依次对于2~n的每一个质数pi,指定一盏亮着的灯ai,点亮所有标号为$A[i]\pm kP_i$的灯. 有spj,任意一种方案即可. 输入一个整数n,输出点灯方案. Solution 首先写个暴力,考虑一下小范围的数据. 我们发现$n<16$的时候没有完美解,都是n-1.再算下去,发现$n>16$的时候任意一组都有完美解. 我们分析一下这个玩意儿. 把一到n的灯集体下标前移1,变成0~n-1.这时候当我们点

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.

【2016北京集训测试赛(七)】自动机 (思考题)

Time Limit: 1000 ms Memory Limit: 256 MB Description Solution 这是一道看起来令人毫无头绪的题,然而确实十分简单巧妙TAT. 题目要求所有点执行相同指令后都回到初始状态. 我们先来考虑只有两种状态的情况:初始状态$T_0$与另一个状态$T_x$. 这样,我们可以通过一个二元记忆化深搜,来得到一种方案A,使得$T_0$回到$T_0$,且$T_x$回到$T_0$.如果这个方案都不存在,那么此时无解. 现在我们知道,执行方案A后,$T_x$与

【2016北京集训测试赛】river

HINT 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. [吐槽] 嗯..看到这题的想法的话..先想到了每个点的度为2,然后就有点不知所措了 隐隐约约想到了网络流,但并没有继续往下想了... 听完学长的讲评之后(%xj)个人觉得建图还是很有意思的ovo [题解] 因为每个点到对面都有k种方式,那就想到每个点原来的点$x_0$拆成k个点$x_1$, $x_2$, $x_3$... $x_k$ 然后很自然地$x_0$和拆成的点之间要连边 容量的话,因为hint里面的限制

2016北京集训测试赛(十七)- 小结

先说一下总体的情况. 场上期望得分 50 + 40 + 30 = 120 , 最后得分 50 + 0 + 30 = 80 , 实际上自己能力所及能做到的 50 + 65 + 30 = 145 分. 第二题爆零是因为我开始写了一个做法, 后来发现这个做法是错的, 然后开始随机化, 调着调着突然发现只有一分钟了, 然后自己把之前调的改回来, 然后发现怎么全都输出 0 ??? Excuse me ?? 原本不用随机化的做法可以拿 40 分, 如果结合上暴力就有 65 了. 这几天打起比赛来还是暴露了许

【2016北京集训测试赛】azelso(unfinished)

[吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难得的一道搞得比较清楚的概d题目吧记录一下还是挺有意思的ovo 当然咯..显然考场上并没有推出来..嗯qwq [题解] 看到说要求期望的距离,然后总的长度又被分成了一段一段的(各个事件) 所以就有一个比较直接的想法:将每一段期望走的次数算出来然后再乘上每一段的距离,加起来就是答案啦 那么现在问题来了怎