题目大意
一条直线上有n个人,第i个的初始位置是pi,做速度为vi的匀速运动(速度是矢量)。给定一个k,你最多可以去掉k个人,然后在T时间后有两个人相遇。最大化这个T。如果可以永远不相遇输出Forever
1≤k≤n≤100000 |pi|,|vi|≤ 109
分析
首先可以看出答案是可以二分的。
接下来的思路非常重要。
考虑对于当前二分的答案mid,两个人满足什么条件会相遇,容易得到是:pi+vi*mid≥pj+vj*mid 且 pi < pj
先给每个人按pi排序,然后得到的序列就是pi从小到大的。接下来又要满足上式。为了使消除的人数尽量少,问题就相当于求最长上升子序列了。
时间复杂度 O(nlog2n)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100005;
typedef long long LL;
typedef double db;
const db z=1e-4,R=2e9+1,INF=1e9*(R+1);
int n,m,p[N],v[N],Left,Right,Mid;
db l,r,mid,f[N];
struct data
{
int p,v;
}A[N];
char c;
int read()
{
int x=0,sig=1;
for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x*sig;
}
bool cmp(data a,data b)
{
return a.p<b.p;
}
int main()
{
freopen("monument.in","r",stdin); freopen("monument.out","w",stdout);
n=read(); m=read();
for (int i=1;i<=n;i++)
{
A[i].p=read(); A[i].v=read();
}
sort(A+1,A+n+1,cmp);
for (int i=1;i<=n;i++)
{
p[i]=A[i].p; v[i]=A[i].v;
}
for (l=0,r=R,mid=(l+r)/2;r-l>=z;mid=(l+r)/2)
{
memset(f,127,sizeof(f));
f[n]=-INF;
for (int i=1;i<=n;i++)
{
db t=p[i]+v[i]*mid;
for (Left=1,Right=n,Mid=Left+Right>>1;Left<Right;Mid=Left+Right>>1)
if (f[Mid]>t) Left=Mid+1;else Right=Mid;
if (f[Left]<t) Left--;
if (Left>0) f[Left]=t;
}
if (f[m]>INF) r=mid;else l=mid;
}
if (r==R) printf("Forever\n");else printf("%.4lf\n",mid);
fclose(stdin); fclose(stdout);
return 0;
}