• CF1388E Uncle Bogdan and Projections 题解


    Codeforces
    Luogu

    Description.

    给定一些和 \(x\) 轴平行的线段,保证两两不交。
    现在你需要找到一个向量,使得他们按照这个向量的方向都平移到 \(x\) 轴上,还是两两不交。
    最小化平移后最左端点到最右端点的距离,输出这个最小值

    Solution.

    没想到凸性质,以为总共可行点是 \(O(n)\) 的。

    首先,我们考虑枚举向量的角度,这个东西是无限的。
    我们考虑无限转有限,因为必定是两个线段他们“擦过”时才可能取最小值。
    所以我们直接暴力找出 \(O(n^2)\) 个有机会取到最小值得地方。
    然后,我们发现,两个不在同一条直线上的线段,他们平移后,取得两个端点之间肯定是无解的。
    所以我们考虑“扫描线”(CF 官方题解用词,但我觉得这根本算不上吧),把不可能的直接删掉。
    就是排序后,如果当前这个点不管不插入或插入都被一个区间覆盖着,就显然不可行。

    然后,我们得到了 \(O(n^2)\) 个可行的点,每判断一个最小值是 \(O(n)\) 的,总复杂度 \(O(n^3)\) 无法通过。
    所以我们可以考虑有无什么特殊性质。
    我们发现了,它具有凸性质!! 赶快上 wqs 二分
    直接三分,我们发现复杂度很优良,达到了 \(O(n\log (n^2))=O(n\log n)\),可以通过此题。
    所以这是碾标了?

    Coding.

    点击查看拉代码
    //是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
    #include<bits/stdc++.h>
    using namespace std;typedef long long ll;
    template<typename T>inline void read(T &x)
    {
    	x=0;char c=getchar(),bz=0;
    	for(;c<48||c>57;c=getchar()) if(!(c^45)) bz=1;
    	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    	bz?x=-x:x;
    }/*}}}*/
    int n,L[2005],R[2005],Y[2005],at,bt;double b[4000005];const double eps=1e-9;
    struct node{double ps;int vl;char operator<(node b) const {return ps<b.ps;}}a[4000005];
    inline double chk(double vl)
    {
    	double mn=1e13,mx=-1e13;
    	for(int i=1;i<=n;i++) mn=min(mn,L[i]-vl*Y[i]),mx=max(mx,R[i]-vl*Y[i]);
    	return mx-mn;
    }
    int main()
    {
    	read(n);for(int i=1;i<=n;i++) read(L[i]),read(R[i]),read(Y[i]);
    	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(Y[i]<Y[j])
    		a[++at]=(node){1.0*(L[j]-R[i])/(Y[j]-Y[i])+eps,1},a[++at]=(node){1.0*(R[j]-L[i])/(Y[j]-Y[i])-eps,-1};
    	sort(a+1,a+at+1);if(!at) a[++at]=(node){0,0};
    	//for(int i=1;i<=at;i++) printf("%.10lf %d\n",a[i].ps,a[i].vl);
    	for(int i=1,v=0;i<=at;v+=a[i++].vl) if(!v||!(v+a[i].vl)) b[++bt]=a[i].ps;
    	//for(int i=1;i<=bt;i++) printf("%lf%c",b[i],i==bt?'\n':' ');
    	int l=1,r=bt;while(l+2<r) {int md1=(l+r)>>1,md2=md1+1;if(chk(b[md1])<chk(b[md2])) r=md2;else l=md1;}
    	double rs=1e13;for(int i=max(1,l-1);i<=min(r+2,bt);i++) rs=min(rs,chk(b[i]));
    	return printf("%.10lf\n",rs),0;
    }
    
  • 相关阅读:
    shell 模式匹配:case
    知识目录总结
    【运维--系统】nacos介绍和安装
    【运维--监控】zabbix自定义发现策略
    【转】在CentOS 8 / RHEL 8上配置主/从BIND DNS服务器
    【运维--安全相关】cerbot证书自动化续期
    【转】Docker 核心技术与实现原理
    【转】Kubernetes scheduler学习笔记
    [转]自建CDN防御DDoS
    【转】Dockerfile
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15114357.html
Copyright © 2020-2023  润新知