• 【北京集训测试赛(五)/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
    ");
    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
    ");
    78         else printf("Alice
    ");
    79     }
    80 }

    Method #3

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

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

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

    $c_icap(x=now)={c_i.ypm 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
    ");
    77         else printf("Alice
    ");
    78     }
    79     return 0;
    80 }
  • 相关阅读:
    JS-两个空数组为什么不相等?
    ES6---箭头函数()=>{} 与function的区别(转载)
    SASS用法指南
    scss/less语法以及在vue项目中的使用(转载)
    基于vue+mint-ui的mobile-h5的项目说明
    vue中mint-ui的filed的与blur事件结合实现检查用户输入是否正确
    Carrierwave 如何配置合理的上传文件名(转自李华顺)
    ruby大神与菜鸟的代码区别
    用imageMagick合成图片添加图片水印
    想做喜欢的安卓应用
  • 原文地址:https://www.cnblogs.com/skylynf/p/7324440.html
Copyright © 2020-2023  润新知