Bzoj3663/4660 CrazyRabbit

题意:给定平面上一个圆和一堆圆外的点,要求选出尽可能多的点使得它们之间两两连线都不和圆相交。保证任意两点连线不和圆相切。点数<=2000

这题是很久以前在某张课件上看见的。看了题解还搞了三小时,联赛滚粗既视感。

有个结论我是直接看的课件:“点和点连线不与圆相交”对应“这两个点所对的圆上的极角序区间相交但不相互包含”,并不会证,画画图好像是对的。

在直线上选择一些两两相交且不包含的区间[L1,R1],[L2,R2],[L3,R3]….,按左端点排序之后,必然有L1<L2<L3….<Ln<R1<…<Rn,然后课件让我枚举第一个区间是谁,按左端点排序后对右端点跑LIS,O(N^2logN). 这里认为左端点可以在圆上所在区间里逆时针走到所在区间的右端点。

但是我写了一发过不了样例,因为在一个环上选一些两两相交不重合的区间,并不一定存在一个区间把其他区间的左端点都包含起来,比如说把圆三等分成三个区间,再把每个区间稍微左右延长一点点。

懵逼…不过仔细观察,发现这个时候如果选了一个起始区间直接跑LIS,出错时一定是有一个区间左端点不被选中的起始区间包含,但这个区间的右端点绕回了起始区间。那么我们把这个区间变成它在圆上的补区间,好像就挺对的。

所以我们把“左端点不在起始区间但右端点在起始区间内”的区间变成它的补区间,也就是把左右端点交换一下,然后跑LIS就A了。这么做的原理是假如区间A和区间B相交且不包含,那么区间A的补区间也一定和区间B相交且不包含。但是,区间A,B都取补区间后不一定相交且不包含。所以跑完一遍LIS还得把交换过的左右端点恢复回去,实现时我写了一发时间戳标记每个区间是否被交换。

PS. 极角序区间预处理的时候注意所求极角的范围。

/**************************************************************
    Problem: 4660
    User: liu_runda
    Language: C++
    Result: Accepted
    Time:5540 ms
    Memory:944 kb
****************************************************************/

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2005;
const double pi=3.14159265;
struct point{
    double x,y;
    void read(){
        scanf("%lf %lf",&x,&y);
    }
    point(){
    }
    point(double _x,double _y){
        x=_x;y=_y;
    }
    point operator-(const point &B)const{
        return point(x-B.x,y-B.y);
    }
}P[maxn];
double l[maxn],r[maxn];
double angle(point A){
    return atan2(A.y,A.x);
}
int seq[maxn];int tot=0;
bool betw(double l,double r,double x){
    if(l<r){
        return l<x&&x<r;
    }else{
        return (x>l)||(x<r);
    }
}
bool cmp1(const int &a,const int &b){
    return l[a]<l[b];
}
double curl,curr;
bool cmp2(const int &a,const int &b){
    if(a>curl&&b>curl)return l[a]<l[b];
    else if(a<curr&&b<curr)return l[a]<l[b];
    else return l[a]>curl;
}
double m[maxn];
double _r[maxn];
int lis(){
    memset(m,0x42,sizeof(m));
    m[0]=-1e30;
    for(int i=1;i<=tot;++i){
         _r[i]=r[seq[i]]-curl;
         if(_r[i]<0)_r[i]+=2*pi;
    }/*
    for(int i=1;i<=tot;++i)printf("%.2f ",_r[i]);
    printf("\n");
    for(int i=1;i<=tot;++i){
        printf("%.2f %.2f\n",l[seq[i]],r[seq[i]]);
    }*/

    int ans=0;double *pt;
    for(int i=1;i<=tot;++i){
        pt=upper_bound(m,m+tot+1,_r[i]);
        if((pt-m)>ans)ans=pt-m;
        *pt=_r[i];
    }
    return ans;
}
int T;
int mark[maxn];
int main(){
   // freopen("4660.in","r",stdin);
  //  freopen("4660.out","w",stdout);
    int n,R;
    scanf("%d%d",&n,&R);
    for(int i=1;i<=n;++i)P[i].read();
    double tmp,delta;
    for(int i=1;i<=n;++i){
        tmp=angle(P[i]);delta=fabs(acos(R/sqrt(P[i].x*P[i].x+P[i].y*P[i].y)));
       // printf("%.2f %.2f\n",tmp,delta);
        l[i]=tmp-delta;r[i]=tmp+delta;
        if(l[i]<-pi)l[i]+=pi*2;
        if(r[i]>pi)r[i]-=pi*2;
    }//printf("\n");
   /* for(int i=1;i<=n;++i){
        printf("%.2f %.2f\n",l[i],r[i]);
    }//while(1);
    printf("\n");*/
    int ans=0,curans;
    for(int i=1;i<=n;++i){//枚举起点
        tot=0;
        ++T;
        seq[++tot]=i;
        for(int j=1;j<=n;++j){
            if(i!=j&&betw(l[i],r[i],l[j])&&!betw(l[i],r[i],r[j]))seq[++tot]=j;
            else if(i!=j&&betw(l[i],r[i],r[j])&&!betw(l[i],r[i],l[j])){
                swap(l[j],r[j]);seq[++tot]=j;
                mark[j]=T;
            }
        }
        curl=l[i];curr=r[i];
        if(l[i]<r[i])sort(seq+1,seq+tot+1,cmp1);
        else sort(seq+1,seq+tot+1,cmp2);
        curans=lis();
        if(curans>ans)ans=curans;
        for(int j=1;j<=tot;++j){
            if(mark[j]==T)swap(l[j],r[j]);
        }
    }
    printf("%d\n",ans);//while(1);
    return 0;
}

/*
5 8
-7 20
10 80
-7 26
-13 79
11 81
*/

  

时间: 2024-12-28 02:02:46

Bzoj3663/4660 CrazyRabbit的相关文章

UVALive 4660 A+B

Regionals 2009 >> Asia - Jakarta 题链接:UVALive 4660 A+B.入门训练题,用C语言编写程序. 这是一个进制问题,输入的两个数不知道是几进制,也就是几进制都可以,求的是两个数最小的和,结果以10进制输出. 对于输入的两个数来说,进制越小值越小,所以尽可能选进制小的.最小的可能的进制就是一串数字中最大的数字加上1.只要两个数的进制知道了,事情就好办了. 另外,需要详细知道函数atoi()的原理,才能够随心所欲地解决进制有关的问题. 程序中,使用字符流读

bzoj 3768: spoj 4660 Binary palindrome二进制回文串

Description 给定k个长度不超过L的01串,求有多少长度为n的01串S满足: 1.该串是回文串 2.该串不存在两个不重叠的子串,在给定的k个串中. 即不存在a<=b<c<=d,S[a,b]是k个串中的一个,S[c,d]是k个串中的一个 (It does not contain two non-overlapped substrings in the given list of K binary strings.) 举个例子,若给定2(k=2)个01串:101和1001 1010

bzoj 4660

三倍经验...(然而我并没有氪金所以只能刷一倍...) 考虑在什么情况下两点是合法的: 可以看到,对于红色的点而言,绿色的点是合法的,而黄色的点是不合法的 那么观察一下这几个点的切线把圆分成的这几个弧之间的关系,可以看到:如果两个弧相交但不包含,那么对应的两点合法(比如红色和绿色),剩余情况均不合法! 于是问题就转化成了圆上有很多段弧,求最多有多少个弧之间两两相交不包含 环上的东西不好做,我们考虑在线段上讨论这个问题 依据弧度制,可以把环上两点对应的圆心角弧度映射到一个数轴上,就变成了区间之间的

pwnable.kr-fd-Writeup

html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption

神奇的scanf

神奇的scanf 作为标准输入输出函数组中的一个重要的输入的函数,scanf/sscanf/vscanf函数和printf/sprintf/vsprintf有个重要的区别:如果格式参数和后面的参数不匹配,printf系列函数可能会导致打印出的格式或者数据不是自己期望的 ,而scanf系列函数如果格式参数和后面的参数不匹配,可能导致有待输入的参数附近的内存发生变化,甚至导致程序崩溃. 以下面的函数为例: 8 #include<stdio.h> 9 #include<string.h>

Nginx 的线程池与性能剖析

http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt158 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为此,NGINX工作在非阻塞的socket模式下,并使用了epoll 和 kqueue这样有效的方法. 因为满负载进程的数量很少(通常每核CPU只有一个)而且恒定,所以任务切换只消耗很少的内

Eclipse报错:java.lang.ClassNotFoundException: ContextLoaderListener(Maven工程)

Eclipse中tomcat部署工程启动后报错: 严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener at org.apache.catalina.loader.W

JQUERY省、市、县城市联动选择

JQUERY 插件开发——CITYLINKAGE(省.市.县城市联动选择) 第一部分:背景   开发源于需求,本次城市联动选择插件算是我写插件的一个特例吧,不是我目前工作需要些的,算是兴趣驱使吧.之前呢,一直想写这个插件,然后错过了一个写这个插件的机会(这个得回顾到很久以前了...此处省去N个字).然后最近“瘾”又犯了,呵呵,随手就拿这个“欠”了很久的插件开刀了.大家都应该知道“某宝”的这个插件写的还是很强大的,支持到街道(镇)级别...可见他们维护的前端数据有多大...不过呢,临渊羡鱼,不如退

Google Play 服务提示更新、Google play 闪退(二)

其实出现这些问题的根本原因是因为:你的Google Play 服务安装的版本不对. 其实,根据Google给出的升级notes: Uploader's notes: To figure out the right version of Google Play services for your Android device, go to Settings -> Apps -> Google Play services, and look at the last 3 numbers in the