1. 程式人生 > >隔熱板 - 二分 - 倍增

隔熱板 - 二分 - 倍增

題目大意:平面上n個點,你要畫m條直線,使得任意一個點和原點的連線與這m條直線中的至少一條相交(不考慮一些亂七八糟的重合情況),並且使得原點到m條直線的最小距離最大。 n 1 0 4 n\le10^4


題解:先二分答案,問題轉為每個區間至少一個點,去掉包含並倍長環,在上面做貪心,用倍增維護即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;typedef double db;const int N=80010,LOG=16;const db eps=0.00065,PI=acos(-1);
inline int inn() { int x;scanf("%d",&x);return x; } inline db indb
() { double x;scanf("%lf",&x);return x; } struct S{ db l,r;int id; S() {} S(db _l,db _r,int _id) { l=_l,r=_r,id=_id; } bool operator<(const S &s)const { return r==s.r?l<s.l:r<s.r; }bool operator==(const S &s)const { return id==s.id; } }s[N];db x[N],y[N],a[N];int nxt[N][LOG]; inline
int check(db R,int n,int m,db alp=0.0,db d=0.0,db bt=0.0) { rep(i,1,n) alp=a[i],d=sqrt(x[i]*x[i]+y[i]*y[i]),bt=acos(R/d),s[i]=S(alp-bt,alp+bt,i),s[i+n]=S(alp-bt+2*PI,alp+bt+2*PI,i+n); sort(s+1,s+n*2+1);rep(i,1,n*2) if(s[i+1].l<=s[i].l) s[i+1]=s[i];int cnt=(int)(unique(s+1,s+2*n+1)-s-1),ans=cnt/2,t=cnt/2;if(ans<=m) return 1; for(int i=1,j=1;i<=cnt;nxt[i++][0]=j) while(j<cnt&&s[i].r>=s[j].l) j++;rep(j,1,LOG-1) rep(i,1,cnt) nxt[i][j]=nxt[nxt[i][j-1]][j-1]; for(int x=1,y,v,i;x<=t;x++) { for(y=x,v=0,i=LOG-1;i>=0;i--) if(nxt[y][i]<x+t) y=nxt[y][i],v+=(1<<i);ans=min(ans,v+1);if(ans<=m) return 1; } return 0; } int main() { int n=inn(),m=inn();db L=eps,R=2e8;rep(i,1,n) x[i]=indb(),y[i]=indb(); rep(i,1,n) { a[i]=atan2(y[i],x[i]);if(a[i]<0) a[i]+=2*PI; }rep(i,1,n) R=min(R,sqrt(x[i]*x[i]+y[i]*y[i])); for(db mid=(L+R)/2;L+eps<=R;mid=(L+R)/2) if(check(mid,n,m)) L=mid+eps;else R=mid-eps;return !printf("%.2lf\n",(double)((L+R)/2)); }