1 条题解

  • 0
    @ 2025-8-24 21:49:44

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ycyaw
    无他,唯勤尔

    搬运于2025-08-24 21:49:44,当前版本为作者最后更新于2019-09-20 13:49:10,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    显然的二分答案。

    对于一个二分出的答案midmid,我们要使分成的每一段中所有点离中心点的距离都小于等于midmid,当然每一段的点是越多越好。

    一段点的最小圆覆盖,用随机增量法可以做到O(len)O(len),不会的先去做最小圆覆盖

    所以难点就是怎么写checkcheck函数。

    以找第一个连续的最长段为例,当然可以一个一个枚举过去找到最远的右端点,但是发现如果第一段就是[1,n][1,n]的段的话,这样做的复杂度就达到O(n2)O(n^2)

    考虑优化一下,对于一个左端点,可以二分一下右端点,这样看起来复杂度更优一点,但是考虑如果每一段长度都为11,那么每次都要二分loglog次,总的是O(n2 log n)O(n^2\ log\ n),显然也不行。

    发现复杂度瓶颈在于每次二分得到的区间长是O(n)O(n)级的,那么可不可以对于一个左端点,快速的找到右端点的范围呢?我们考虑倍增,设左端点为ll,我们倍增求出右端点的范围。设倍增得到最大的满足条件的区间长度为2k2^k,那么我们二分的左端点为i+2k1i+2^k-1,右端点为min(n,i+2k+11)min(n,i+2^{k+1}-1),在这段区间内二分,显然复杂度就正确了。

    这题好像很卡精度,随机增量法用不同的随机种子还会WAWA。如果只WAWA一两个点,考虑换个随机种子。

    总复杂度O(n log2 n)O(n\ log^2\ n),但是常数很大所以跑得巨慢。

    Code Below:Code\ Below:

    #include<bits/stdc++.h>
    #define ts cout<<"ok"<<endl
    #define ll long long
    #define hh puts("")
    #define pc putchar
    //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    //char buf[1<<21],*p1=buf,*p2=buf;
    using namespace std;
    const int N=100005;
    int n,m,res[N][2],cnt,ci;
    double eps=1e-10,R;
    struct point{
        double x,y;
    }a[N],b[N],O;
    inline int read(){
        int ret=0,ff=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') ff=-ff;ch=getchar();}
        while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
        return ret*ff;
    }
    void write(int x){
        if(x<0){x=-x;putchar('-');}
        if(x>9) write(x/10);
        putchar(x%10+48);
    }
    void writeln(int x){write(x),hh;}
    point Mid(point A,point B){
        return (point){(A.x+B.x)/2,(A.y+B.y)/2};
    }
    double dist(point A,point B){
        return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
    }
    void getO(point A,point B,point C){
        double aa,bb,cc,dd,ee,ff;
        aa=A.x-C.x;bb=A.y-C.y;
        cc=B.x-C.x;dd=B.y-C.y;
        ee=A.x*A.x+A.y*A.y-C.x*C.x-C.y*C.y;
        ff=B.x*B.x+B.y*B.y-C.x*C.x-C.y*C.y;
        O.x=(dd*ee-bb*ff)/(2*aa*dd-2*bb*cc);
        O.y=(aa*ff-cc*ee)/(2*aa*dd-2*bb*cc);
        R=dist(O,A);
    //    printf("%.5lf %.5lf",O.x,O.y),hh;
    //    printf("%.5lf %.5lf %.5lf",dist(O,A),dist(O,B),dist(O,C));
    }
    void work(int l,int r){//随机增量求最小圆覆盖,O(r-l+1)
        int tot=0;
        for(int i=l;i<=r;i++) b[++tot]=a[i];
        for(int i=1;i<=tot;i++) swap(b[i],b[rand()%tot+1]);
        O=b[1],R=0;
        for(int i=1;i<=tot;i++){
            if(dist(b[i],O)>R+eps){//i不在圆内 
                O=b[i],R=0;
                for(int j=1;j<i;j++){
                    if(dist(b[j],O)>R+eps){//j不在圆内
                        O=Mid(b[i],b[j]);
                        R=dist(O,b[i]);
                        for(int k=1;k<j;k++)
                            if(dist(b[k],O)>R+eps)
                                getO(b[i],b[j],b[k]);
                    }
                }
            }
        }
    }
    bool check(double lim){
        cnt=0;
        int ans;
        for(int i=1;i<=n;i=ans+1){
            int k;
            for(k=1;i+(1<<k)-1<=n;k++){//k=0显然可行,从1开始 
                work(i,i+(1<<k)-1);
                if(R>lim+eps) break;
            }
            ans=i;
            int l=i+(1<<(k-1))-1,r=min(n,i+(1<<k)-1);
            while(l<=r){
                int mid=(l+r)>>1;
                work(i,mid);
                if(R<lim+eps) l=mid+1,ans=mid;
                else r=mid-1;
            }
            cnt++;
            res[cnt][0]=i,res[cnt][1]=ans;
            if(cnt>m) return 0;
        }
        return 1;
    }
    signed main(){
        srand(20031128);
        n=read(),m=read();
        for(int i=1;i<=n;i++){
            a[i].x=read();
            a[i].y=read();
        }
        work(1,n);
        double l=0,r=R;
        if(m>1){
            ci=50;
            while(ci--&&r-l>eps){
                double mid=(l+r)/2;
                if(check(mid)) r=mid;
                else l=mid;
            }
        }
        check(r);
        printf("%.8lf\n",r);
        writeln(cnt);
        for(int i=1;i<=cnt;i++){
            work(res[i][0],res[i][1]);
            printf("%.8lf %.8lf\n",O.x,O.y);
        }
        return 0;
    }
    
    • 1

    信息

    ID
    2593
    时间
    5000ms
    内存
    125MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者