G - Freestyle Editorial by en_translator
First, consider the case where Takahashi can swim only in the following two ways:
- Consume stamina of \(A_1\) to advance \(B_1\) meters per second
- Consume stamina of \(A_2\) to advance \(B_2\) meters per second
In this case, he may combine these styles to swim in the following way:
- Consume stamina of \(tA_1+(1-t)A_2\) to advance \(tB_1+(1-t)B_2\) meters per second.
In other words, if \(B\) is attributed to the \(x\) axis and \(A\) to the \(y\) axis, plotting \((B_1,A_1)\) and \((B_2,A_2)\), he can swim in a style for any point on the segment connecting these points.
What if we have more points?
In fact, if Takahashi can swim in styles corresponding to \(N\) points \((B_i,A_i)\), he can swim in a style for any point on the segment in their convex hull’s interior or circumference, but not for any point outside.
Brief proof: first, by the discussion of the segment case, any point on the circumference of the convex hull can be obtained. Any point in its interior can also be obtained by choosing two points on the circumference as if filling the region with segments. Conversely, if a point outside the convex hull can be constructed, that means there is a point outside the convex hull, which is a contradiction.
Let us plot the styles on the \(xy\)-plane and proceed the discussion.
- Let us call the point with the maximum \(x\) coordinate (largest \(B_i\)) the most time-efficient point. If he can always choose that style (without running out of his stamina), that is the optimal strategy.
- Let us call the point with the minimum \(A_i / B_i\) the most stamina-efficient point. If he cannot swim \(D_i\) meters in that style, he can never swim \(D_i\) meters in any way, so the answer is
-1
.
For example, in the sample, the most time-efficient point \(P_\omega\) is \((B,A) = (4,4)\), and the most stamina-efficient point \(P_\\alpha\) is \((B,A)=(2,1)\).
From now on, we assume that he can swim \(D_i\) meters at the most time-efficient point, but cannot at the most stamina-efficient point.
In this circumstance, he needs to swim \(D_i\) meters within stamina \(C_i\). Let us draw this information on the plot.
Specifically, draw a line that passes through \((0,0)\) and \((D_i,C_i)\). If he chooses a swimming style for any point on or below this line, he can always swim \(D_i\) meters below total stamina consumption of \(C_i\). Otherwise, if he choose a style for any point above it, he cannot finish swimming \(D_i\) meters.
Subject to this constraint, we need to find the rightmost point among those in which he can swim \(D_i\) meters. This is the intersection of the circumference of the convex hull and the line with the maximum \(x\) coordinate.
(The following figure illustrates the fifth query of the sample.)
When it comes to finding such a point (without much effort), you can use a library that finds the intersections of a convex polygon and a line (explanation in Japanese).
Here, we consider a bit more how to reduce implementation.
Using the fact that he can finish at the most stamina-efficient point \(P_\alpha\) but not at the most time-efficient point \(P_\omega\), consider tracing the convex hull from \(P_\alpha\) to \(P_\omega\) counterclockwise.
Then, he can reach the goal between \(P_\alpha\) and the intersection \(P_{opt}\), but not between \(P_{opt}\) and \(P_\omega\).
Thus, one can do binary search for which segment \(P_{opt}\) is on, then do another binary search for where \(P_{opt}\) is located at on that segment, in order to find the coordinates of \(P_{opt}\).
Sample code (C++):
#include<bits/stdc++.h>
using namespace std;
using pl=pair<long long,long long>;
long long crs(pl a,pl b){
return a.first*b.second-a.second*b.first;
}
pl sub(pl a,pl b){
return {a.first-b.first,a.second-b.second};
}
bool plcomp(pl a,pl b){
return (a.first*b.second < b.first*a.second);
}
int main(){
long long n;
cin >> n;
set<pl> st;
for(long long i=0;i<n;i++){
pl ca;
cin >> ca.second >> ca.first;
st.insert(ca);
}
n=st.size();
vector<pl> a;
for(auto &nx : st){a.push_back(nx);}
// convex hull
// ref. https://github.com/E869120/kyopro_educational_90/blob/main/sol/041-03.cpp
if(n>=3){
vector<pl> g1,g2;
g1.push_back(a[0]);
g1.push_back(a[1]);
g2.push_back(a[0]);
g2.push_back(a[1]);
for(long long i=2;i<n;i++){
while(g1.size()>=2 && crs(sub(g1[g1.size()-1],g1[g1.size()-2]),sub(a[i],g1[g1.size()-1]))<=0){g1.pop_back();}
while(g2.size()>=2 && crs(sub(g2[g2.size()-1],g2[g2.size()-2]),sub(a[i],g2[g2.size()-1]))>=0){g2.pop_back();}
g1.push_back(a[i]);
g2.push_back(a[i]);
}
a.clear();
for(long long i=0;i<g1.size();i++){a.push_back(g1[i]);}
for(long long i=(long long)g2.size()-2;i>=1;i--){a.push_back(g2[i]);}
}
n=a.size();
long long omega=0,alpha=0;
for(long long i=0;i<n;i++){
if(a[omega].first<a[i].first){
omega=i;
}
if(plcomp(a[alpha],a[i])){
alpha=i;
}
}
long long q;
cin >> q;
while(q>0){
q--;
long long pow,dist;
cin >> pow >> dist;
// if(pow/a[alpha].second*a[alpha].first<dist){}
if(pow*a[alpha].first<dist*a[alpha].second){
cout << "-1\n";
continue;
}
// if(pow/a[omega].second*a[omega].first>=dist){}
if(pow*a[omega].first>=dist*a[omega].second){
double cres=dist;
cres/=a[omega].first;
cout << fixed << setprecision(12) << cres << "\n";
continue;
}
long long p1,p2;
{
long long lef=alpha;
long long rig=omega;
if(lef>rig){rig+=n;}
while(lef<=rig){
long long te=(lef+rig)/2;
long long i=te%n;
if(pow*a[i].first>=dist*a[i].second){lef=te+1;}
else{rig=te-1;}
}
p1=(rig%n);
p2=(lef%n);
}
double rv=((double)dist)/((double)a[p1].first);
double lef=0.0,rig=1.0;
for(long long tr=0;lef<=rig && tr<100;tr++){
double te=(lef+rig)/2.0;
double fir=(1.0-te)*((double)a[p1].first)+te*((double)a[p2].first);
double sec=(1.0-te)*((double)a[p1].second)+te*((double)a[p2].second);
if(((double)pow)*fir>=((double)dist)*sec){
rv=min(rv,((double)dist)/((double)fir));
lef=te;
}
else{
rig=te;
}
}
cout << fixed << setprecision(12) << rv << "\n";
}
return 0;
}
posted:
last update: