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
*/

  

posted @ 2016-10-15 10:49  liu_runda  阅读(504)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难