题解思路:
首先,我们很容易知道一个点能当第一的条件是它在超过前面所有人之前不能被他后面的人超过。那么先考虑哪些点能超过前面的所有点呢?显然需要你的加速度大于前面所有点的加速度,那么我们就可以先按照位置排序,再按照加速度排,只考虑 pos1>pos2 && a1<a2 的点即可。
我们处理出这些点以后应该怎么办呢?朴素想法:枚举上述处理后的每个点,计算自己成为第一的时间t1,再计算后面的点超过自己最短的时间t2,若t1<t2,则会对答案产生贡献。思路很清晰,但是总复杂度为O(n2)。
其实在计算过程中就能意识到这里面由很多无用的计算,比如,我已经知道点3能在点2超过点1之前超过点1,也知道点4不能在点2超过点1之前超过点1,那么我就没必要去计算点3、点4谁能先超过点2,我个人觉得这个逻辑还是比较好理解的。但是在朴素算法中这些无用的计算都存在。那么我们应该怎样取优化它呢?
优化算法:利用栈后进先出的性质,对n从前往后枚举,每次比较时取栈顶两个点u1、u2,若 t(u1->u2)<t(i ->u2)则说明当前点i不能在u1到达u2前到达u2,因此push(i),点i成为新的栈顶;若 t(u1->u2)>=t(i ->u2)则说明当前点i能在u1到达u2前到达u2,因此pop(u1),因为u1能被点i超过,不可能成为第一,然后对栈内元素继续如上所述操作下去。
对上述算法没那么理解的可以看一下我的代码。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> PII; #define ls l,mid,rt<<1 #define rs mid+1,r,rt<<1|1 #define endl '\n' #define p4 puts("444") const int MAXN = 1e5+10; const double Pi = acos(-1.0); const ll mod = 1e9+7; int n,m; struct node{ ll p,a; int vis; }r[MAXN],R[MAXN]; bool cmp(node aa,node bb){ if(aa.p!=bb.p)return aa.p>bb.p; else return aa.a>bb.a; } void solve(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lld %lld",&r[i].p,&r[i].a); sort(r+1,r+n+1,cmp); int cnt=0; for(int i=1;i<=n;i++){ if(R[cnt].a==r[i].a&&R[cnt].p==r[i].p)R[cnt].vis=1; if(R[cnt].a<r[i].a){ cnt++; R[cnt].a=r[i].a; R[cnt].p=r[i].p; R[cnt].vis=0; } } n=cnt; stack<node>st; st.push(R[1]); if(n>1)st.push(R[2]); for(int i=3;i<=n;i++){ while(st.size()>=2){ node u2=st.top();st.pop(); node u1=st.top(); ll t1=(u1.p-R[i].p)*(u2.a-u1.a); ll t2=(u1.p-u2.p)*(R[i].a-u1.a); if(t1>t2){ st.push(u2); break; } } st.push(R[i]); } int ans=0; while(!st.empty()){ node u=st.top();st.pop(); ans+=(u.vis^1); } cout<<ans<<endl; } int main() { int T=1; scanf("%d",&T); while(T--) solve(); }