【2018.07.03NOIP模拟】T3 Problem C
Description
给出平面上n 个点的坐标,和一个半径为R 的圆心在原点的圆。对于两个点,它
们之间有连边,当且仅当它们的连线所在直线与圆不相交。求此图的最大团。
Input
第一行两个整数n 和R, 表示点数和圆的半径。
接下来n 行,每行两个整数xi 和yi,表示第i 个点的坐标
保证每个点都严格在圆外,且两两直线不与圆相切。
Output
输出一个整数,表示最大团的大小。
Sample input
6 3
0 6
-7 -4
-3 -2
7 -5
-2 3
8 -3
Sample output
4
Constraint
30% n<=20
60% n<=100
80% n<=2000
90% n<=5000
100% 1<=n<=20000,|xi|,|yi|,R<=5000
Hint
保证数据随机
贴一波正解:
过每个点i做圆的两条切线,得到两个切点间的一段弧[li,ri]。两点连线所在直线与圆不相交,当且仅当两段弧相交且不包含,即 li < lj < ri < rj 。
n个点满足l1
#include<bits/stdc++.h>
using namespace std;
#define N 20010
const double pi=acos(-1.0);
int n,ans=1,pre_len;
double x[N],y[N],p[N],R;
struct Node{double l,r;}pre[N],tmp[N];
bool cmp(Node a,Node b){return a.l<b.l;}
int solve(double a,int len){
int l=1,r=len,res=len+1;
while(l<=r){
int mid=(l+r)>>1;
if(p[mid]>a)res=mid,r=mid-1;
else l=mid+1;
}
return res;
}
int main(){
scanf("%d%lf",&n,&R);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&x[i],&y[i]);
double t=atan2(y[i],x[i]);
double g=acos(R/sqrt(x[i]*x[i]+y[i]*y[i]));
pre[++pre_len]=(Node){t-g,t+g};
}
for(int i=1,up=pre_len;i<=up;i++){
int tmp_len=0;
tmp[++tmp_len]=(Node){0,pre[i].r-pre[i].l};
for(int j=1;j<=pre_len;j++){
if(i==j)continue;
double pl=pre[j].l-pre[i].l;
double pr=pre[j].r-pre[i].l;
if(pl<0)pl+=pi*2;if(pl>pi*2)pl-=pi*2;
if(pr<0)pr+=pi*2;if(pr>pi*2)pr-=pi*2;
if(pl>pr)swap(pl,pr);
if(pl<tmp[1].r&&pr>tmp[1].r)tmp[++tmp_len]=(Node){pl,pr};
}
sort(tmp+1,tmp+tmp_len+1,cmp);
int new_len=0;
for(int j=1;j<=tmp_len;j++){
int pos=solve(tmp[j].r,new_len);
p[pos]=tmp[j].r;
new_len=max(new_len,pos);
}
ans=max(ans,new_len);
up=min(up,pre_len*5/ans+20);
}
printf("%d",ans);
return 0;
}