Codeforces Round #336 (Div. 1)E:Cross Sum(二分+扫描线)

博客详细解析了Codeforces Round #336 (Div. 1)E题,即'Cross Sum'问题。通过二分查找确定答案,并利用扫描线算法检查特定范围内直线交点数量是否超过给定限制。在检查直线与圆的交点时,通过排序简化计算,使用树状数组高效地维护区间和。
摘要由CSDN通过智能技术生成

传送门

题解:
二分答案,然后检查当前范围(圆)中直线的交点是否超过 m m

在圆中检查两条直线相交非常方便,只需要把直线与圆的交点做出来,相交直线一定形如ABAB的形式。
那么排一边序直接做扫描线,用树状数组维护区间和。

最后查询的时候要查询 leps l − e p s ,因为 l l <script type="math/tex" id="MathJax-Element-194">l</script>的点可能很多。

#include <bits/stdc++.h>
typedef double LD;
using namespace std;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
int n,m;
LD ans;
struct P{
    LD x,y;
    P(LD x=0,LD y=0):x(x),y(y){}
    friend inline P operator -(const P &a,const P &b) {return P(a.x-b.x,a.y-b.y);}
    inline P rev() {return P(-x,-y);}
    inline P rev(LD o) {
        LD sn=sin(o),cs=cos(o);
        return P(x*cs-y*sn,x*sn+y*cs);
    }
    inline LD dis() {return sqrt(x*x+y*y);}
    friend inline LD operator *(const P &a,const P &b) {return a.x*b.y-a.y*b.x;}
}st,line[50010];
bool onleft[50010];
inline bool OnLeft(int i) {
    P a=P(0,line[i].y),b=P(1,line[i].x+line[i].y);
    return (a-st)*(b-st)>0;
}
typedef pair<LD,LD> pii;
const LD PI=acos(-1.0);
const LD PI2=acos(-1.0)*2.0;
inline LD fix(LD x) {
    while(x<-PI) x+=PI2;
    while(x>PI) x-=PI2;
    return x;
}
pii stk[100010];
int tot,bit[100010];
inline int inc(int i,int v) {for(;i<=tot;i+=(i&(-i))) bit[i]+=v;}
inline int ask(int i,int rs=0) {for(;i;i-=(i&(-i))) rs+=bit[i]; return rs;}
inline int calc(LD r) {
    tot=0;
    for(int i=1;i<=n;i++) {
        LD dis=fabs(line[i].x*st.x-st.y+line[i].y)/sqrt(line[i].x*line[i].x+1.0);
        if(dis>r) continue;
        LD ang=asin(dis/r);
        ang=fix(ang);
        if(ang>PI/2) ans=PI/2-ang;
        P tar(100,100*line[i].x);
        if(onleft[i]) tar=tar.rev();
        P t1=tar.rev(ang).rev();
        P t2=tar.rev(-ang);
        LD a1=fix(atan2(t1.y,t1.x));
        LD a2=fix(atan2(t2.y,t2.x));
        if(a1>a2) swap(a1,a2);
        stk[++tot]=make_pair(a1,a2);
        stk[++tot]=make_pair(a2,a1);
    }
    sort(stk+1,stk+tot+1);
    for(int i=1;i<=tot;i++) bit[i]=0;
    long long cnt=0;
    for(int i=1;i<=tot;i++) {
        if(stk[i].second>stk[i].first) inc(i,1);
        else {
            int p=lower_bound(stk+1,stk+tot+1,make_pair(stk[i].second,stk[i].first))-stk;
            cnt+=ask(i)-ask(p);
            inc(p,-1);
        }
    } return cnt;
}
set < pair<LD,int> > S;
typedef set< pair<LD,int> >::iterator it;
inline it nxt(it p) {return ++p;}
inline P inter(int i,int j) {
    LD x=(line[j].y-line[i].y)/(line[i].x-line[j].x);
    return P(x,line[i].x*x+line[i].y);
}
inline LD calc_v(LD r) {
    tot=0; LD cnt=0;
    for(int i=1;i<=n;i++) {
        LD dis=fabs(line[i].x*st.x-st.y+line[i].y)/sqrt(line[i].x*line[i].x+1.0);
        if(dis>r) continue;
        LD ang=asin(dis/r);
        ang=fix(ang);
        if(ang>PI/2) ans=PI/2-ang;
        P tar(100,100*line[i].x);
        if(onleft[i]) tar=tar.rev();
        P t1=tar.rev(ang).rev();
        P t2=tar.rev(-ang);
        LD a1=fix(atan2(t1.y,t1.x));
        LD a2=fix(atan2(t2.y,t2.x));
        if(a1>a2) swap(a1,a2);
        stk[++tot]=make_pair(a1,i+233);
        stk[++tot]=make_pair(a2,a1);
    }
    sort(stk+1,stk+tot+1);
    for(int i=1;i<=tot;i++) {
        if(stk[i].second>stk[i].first) S.insert(make_pair(stk[i].first,(int)(stk[i].second-233+0.1)));
        else {
            it p=S.lower_bound(make_pair(stk[i].second,0));
            if(p==S.end()) continue;
            for(it t=nxt(p);t!=S.end();t++) 
                cnt+=(inter(t->second,p->second)-st).dis();
            S.erase(p);
        }
    } return cnt;
}
int main() {
    n=rd();
    st.x=rd()/1000.0,st.y=rd()/1000.0; 
    m=rd();
    for(int i=1;i<=n;i++) 
        line[i].x=rd()/1000.0, line[i].y=rd()/1000.0;
    for(int i=1;i<=n;i++) onleft[i]=OnLeft(i);
    LD l=0,r=1e10; int c;
    for(int i=1;i<=70;i++) {
        LD mid=(l+r)/2.0;
        if((c=calc(mid))>=m) r=mid;
        else l=mid;
    }
    c=calc(l-(1e-7));
    printf("%.6f\n",(double)(calc_v(l-(1e-7))+(m-c)*l));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值