【Codeforces Round 335 (Div 2)E】【计算几何-凸包 线性规划 三分凸包上最优点】Freelancer's Dreams 二维属性 充最少的钱变得满足要求 [计算几何-凸包模板]
#include<stdio.h> #include<iomanip> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=1e5+10,M=0,Z=1e9+7,ms63=1061109567; struct point { int x,y; point(){} point(int x_,int y_){x=x_;y=y_;} bool operator < (const point& b)const { return x!=b.x?x<b.x:y<b.y; } point operator - (const point& b)const { return point(x-b.x,y-b.y); } LL operator * (const point& b)const { return (LL)x*b.y-(LL)y*b.x; } }a[N]; int s[N]; int n; double X,Y; void solve() { scanf("%d",&n); scanf("%lf%lf",&X,&Y); for(int i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y); sort(a+1,a+n+1); //按照(先x从小到大,后y从小到大)排序 int top=0; for(int i=1;i<=n;i++) { while(top&&a[i].y>=a[s[top]].y)--top; //这是针对这道题目的优化,同时还有着去重点的作用 while(top>=2&&(a[i]-a[s[top-1]])*(a[s[top]]-a[s[top-1]])<=0)--top; s[++top]=i; } double ans=max(X/a[s[1]].x,Y/a[s[1]].y); for(int i=1;i<top;++i) { point p1=a[s[i]]; point p2=a[s[i+1]]; double l=0; double r=1; double tmpl; double tmpr; for(int tim=1;tim<=200;++tim) { double lm=(l+l+r)/3; double rm=(l+r+r)/3; tmpl=max( X/(lm*p1.x+(1-lm)*p2.x), Y/(lm*p1.y+(1-lm)*p2.y) ); tmpr=max( X/(rm*p1.x+(1-rm)*p2.x), Y/(rm*p1.y+(1-rm)*p2.y) ); tmpl<tmpr?r=rm:l=lm; } gmin(ans,tmpl); } printf("%.15f\n",ans); } int main() { solve(); return 0; } /* 【trick&&吐槽】 这题本来看着是线性规划或是计算几何。我是计算几何渣渣,于是一直不敢做。 直到hack被抢,无所适从,才在最后20分钟开始暴力冲一发。 没想到竟然过了pretest,很激动却最后wa on test 75.天啊噜! 不过,这道题教会了我很多做人的道理! 1,学会了——"通过线段两个端点,比例系数之和1可以构成线段之间任意点"的知识(噗,应该是忘掉的高中知识)。 2,知道了自己写了一年的凸包程序是有问题的 凸包经常考虑去重点 凸包可以直接按照(x升序,y升序)排序,然后求一个上凸包和一个下凸包,拼在一起就是答案。 3,对于变量的存储空间,int是4byte,LL是8byte,double也是8byte,long double却只是12byte。 long double的精度,是没有LL高的,能用LL的地方,就不要用long double 4,这道题的变量都在[1,1e6]之间。于是答案的上限也不过只有1e6. 5,真正的错误是我凸包没判重点=w=!比赛的时候只要判重点就能AC了啊! 【题意】 我们初始属性是(0,0),我们想要变强。 可是!不充钱,怎么变得更强? 于是有n(1e5)个充钱大礼包。 对于每个大礼包,可以用1单位价值的钱,获取(a[i].x,a[i].y)的属性提升。a[i].x和a[i].y都是[1,1e6]范围的数。 钱是虚拟货币,所以单位为double。 问你,要至少冲多少钱,才能使得自己的属性变成至少为(X,Y)。 即最后的二维(x,y),要满足x>=X&&y>=Y。 【类型】 计算几何-凸包 【分析】 这题,对于很多大神而言就是一眼题。 因为有一个特别棒的做法。就是先求凸包(实际是上凸包)—— 然后有一个性质,就是,对于一个三角形ABC,我们可以用k*AB+(1-k)*AC来表示出BC上任意一点的坐标。 于是,我们就有—— 对于凸包边界上的所有点,我们都可以用大小为1的成本得到。 于是,我们可以舍弃掉原来有的凸包内部的初始点(因为这些初始点,同样成本为1,得到的价值没有凸包上的点优)。 而凸包外部的点,是我们用1成本达不到的,自然不需要考虑。 于是,这道题,我们求得凸包之后,直接引出一个(X,Y)的向量(更准确地讲,叫做射线), 这个射线与上凸包的交点(设为(x_,y_)),距离圆心的距离设为d, 而(X,Y)这个点,距离圆心的距离设为D, 那么答案就是D/d. 于是,我们可以枚举上凸包的所有边,判定线段交点。 也可以枚举上凸包的所有边,三分答案—— 因为凸包上只有一点,对应于(x_,y_)方向上,权值最小,其它点的权值都是要更大的(权值定义为(max(X/x,Y/y))) 于是,就可以AC这道题啦。 ================================================================================ 然而,我并不知道这个。我只是猜到答案与凸包有关,可能在凸包相邻点对之间。 于是我用了另外一种做法,是一种没有想过正确性的破釜沉舟的"试一试看,反正不花钱噗"的做法。 我的做法是——虚拟一个(0,0)点,求得凸包。 第一种情况,先用凸包上的所有点,用max(X/a[i].x,Y/a[i].y)更新一遍答案 第二种情况,然后认定答案是我们通过 凸包上的相邻点对 的搭配得到, 设是由p1和p2的搭配得到的,即(X,Y)=kp1+wp2 于是我们三分第一个点所选取的分量,即k, 然后算出第二个点,还需要选取的分量,即w。 用第一个点选取量+第二个点选取量的和,即k+w,来决定三分位置的移动。更新答案最后输出。 要不是我凸包求错了,也是可以AC的啦~~ 【时间复杂度&&优化】 我的做法——O(求凸包复杂度+凸包点数*三分次数) 三分凸包上线段的做法——O(求凸包复杂度+凸包点数*三分次数) 求射线与凸包交点做法——O(求凸包复杂度) */
【Codeforces Round 335 (Div 2)E】【凸包 线性规划 原始版本】Freelancer's Dreams 二维属性 充最少的钱变得满足要求
#include<stdio.h> #include<iomanip> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=1e5+10,M=0,Z=1e9+7,ms63=1061109567; struct point { int x,y; point(){} point(int x_,int y_){x=x_;y=y_;} bool operator < (const point& b)const { return x!=b.x?x<b.x:y<b.y; } point operator - (const point& b)const { return point(x-b.x,y-b.y); } LL operator * (const point& b)const { return (LL)x*b.y-(LL)y*b.x; } }a[N]; int s[N]; int n; double X,Y; int i; double cnt(double m) { double x=m*a[s[i]].x; double y=m*a[s[i]].y; if(x>=X&&y>=Y)return m; else { if(x>=X) return m+(Y-y)/a[s[i+1]].y; else if(y>=Y)return m+(X-x)/a[s[i+1]].x; else return m+max((Y-y)/a[s[i+1]].y,(X-x)/a[s[i+1]].x); } } void solve() { scanf("%d",&n); scanf("%lf%lf",&X,&Y); for(int i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y); sort(a+1,a+n+1); //按照(先x从小到大,后y从小到大)排序 int top=0; for(int i=1;i<=n;i++) { while(top&&a[i].y>=a[s[top]].y)--top; //这是针对这道题目的优化,同时还有着去重点的作用 while(top>=2&&(a[i]-a[s[top-1]])*(a[s[top]]-a[s[top-1]])<=0)--top; s[++top]=i; } double ans=max(X/a[s[1]].x,Y/a[s[1]].y); for(i=1;i<top;++i) { point p1=a[s[i]]; point p2=a[s[i+1]]; double l=0; double r=1e6; double tmpl; double tmpr; for(int tim=1;tim<=200;++tim) { double lm=(l+l+r)/3; double rm=(l+r+r)/3; tmpl=cnt(lm); tmpr=cnt(rm); tmpl<tmpr?r=rm:l=lm; } gmin(ans,tmpl); } printf("%.15f\n",ans); } int main() { solve(); return 0; } /* 【trick&&吐槽】 这题本来看着是线性规划或是计算几何。我是计算几何渣渣,于是一直不敢做。 直到hack被抢,无所适从,才在最后20分钟开始暴力冲一发。 没想到竟然过了pretest,很激动却最后wa on test 75.天啊噜! 不过,这道题教会了我很多做人的道理! 1,学会了——"通过线段两个端点,比例系数之和1可以构成线段之间任意点"的知识(噗,应该是忘掉的高中知识)。 2,知道了自己写了一年的凸包程序是有问题的 凸包经常考虑去重点 凸包可以直接按照(x升序,y升序)排序,然后求一个上凸包和一个下凸包,拼在一起就是答案。 3,对于变量的存储空间,int是4byte,LL是8byte,double也是8byte,long double却只是12byte。 long double的精度,是没有LL高的,能用LL的地方,就不要用long double 4,这道题的变量都在[1,1e6]之间。于是答案的上限也不过只有1e6. 5,真正的错误是我凸包没判重点=w=!比赛的时候只要判重点就能AC了啊! 【题意】 我们初始属性是(0,0),我们想要变强。 可是!不充钱,怎么变得更强? 于是有n(1e5)个充钱大礼包。 对于每个大礼包,可以用1单位价值的钱,获取(a[i].x,a[i].y)的属性提升。a[i].x和a[i].y都是[1,1e6]范围的数。 钱是虚拟货币,所以单位为double。 问你,要至少冲多少钱,才能使得自己的属性变成至少为(X,Y)。 即最后的二维(x,y),要满足x>=X&&y>=Y。 【类型】 计算几何-凸包 【分析】 这题,对于很多大神而言就是一眼题。 因为有一个特别棒的做法。就是先求凸包(实际是上凸包)—— 然后有一个性质,就是,对于一个三角形ABC,我们可以用k*AB+(1-k)*AC来表示出BC上任意一点的坐标。 于是,我们就有—— 对于凸包边界上的所有点,我们都可以用大小为1的成本得到。 于是,我们可以舍弃掉原来有的凸包内部的初始点(因为这些初始点,同样成本为1,得到的价值没有凸包上的点优)。 而凸包外部的点,是我们用1成本达不到的,自然不需要考虑。 于是,这道题,我们求得凸包之后,直接引出一个(X,Y)的向量(更准确地讲,叫做射线), 这个射线与上凸包的交点(设为(x_,y_)),距离圆心的距离设为d, 而(X,Y)这个点,距离圆心的距离设为D, 那么答案就是D/d. 于是,我们可以枚举上凸包的所有边,判定线段交点。 也可以枚举上凸包的所有边,三分答案—— 因为凸包上只有一点,对应于(x_,y_)方向上,权值最小,其它点的权值都是要更大的(权值定义为(max(X/x,Y/y))) 于是,就可以AC这道题啦。 ================================================================================ 然而,我并不知道这个。我只是猜到答案与凸包有关,可能在凸包相邻点对之间。 于是我用了另外一种做法,是一种没有想过正确性的破釜沉舟的"试一试看,反正不花钱噗"的做法。 我的做法是——虚拟一个(0,0)点,求得凸包。 第一种情况,先用凸包上的所有点,用max(X/a[i].x,Y/a[i].y)更新一遍答案 第二种情况,然后认定答案是我们通过 凸包上的相邻点对 的搭配得到, 设是由p1和p2的搭配得到的,即(X,Y)=kp1+wp2 于是我们三分第一个点所选取的分量,即k, 然后算出第二个点,还需要选取的分量,即w。 用第一个点选取量+第二个点选取量的和,即k+w,来决定三分位置的移动。更新答案最后输出。 要不是我凸包求错了,也是可以AC的啦~~ 【时间复杂度&&优化】 我的做法——O(求凸包复杂度+凸包点数*三分次数) 三分凸包上线段的做法——O(求凸包复杂度+凸包点数*三分次数) 求射线与凸包交点做法——O(求凸包复杂度) */