P11232 [CSP-S 2024] 超速检测
考场差点切掉,分类讨论讨论少了,被民间数据卡掉最后两个点。
Update
24/10/27 0:12 初稿
P11232 [CSP-S 2024] 超速检测(民间数据)
一条路,长 \(L\) ,路上有 \(n\) 辆车在行驶,其驶入这条路的坐标为 \(d_i\) ,速度为 \(v_i\) ,加速度为 \(a_i\) 。当车的坐标为 \(L\) 或速度为 \(0\) 时,视为驶出这条路。
已知这条路限速 \(V\) ,路上有 \(m\) 个监控, 坐标为 \(p_j\) 。一辆车被认为超速,当且仅当其在某个监控处的速度 \(v_t > V\) 。
求:
- 被认为超速的车有多少辆;
- 在确保所有超速的车都被认为是超速的情况下,可以关掉多少监控。
提示
匀加速直线运动,即为加速度恒定的直线运动。高中的OIer们一定很熟悉。为了让初中或小学的OIer们也能看懂题目,给出几个公式:
- \(v_t=v_0+at\)
- \(x_t=x_0+v_0t+\frac{1}{2}at^2\)
- \({v_t}^2-{v_0}^2=2as\)
其中,本篇题解仅用公式3,因为该公式阐明了速度与加速度、位移之间的关系。
思路
第一问
首先,对于 \(a_i=0\) 的车,若 \(v_i>0\) ,只要其被监控拍到,其即为超速车辆。
由此得出下式:
$is_i = ( v_i > v ) \wedge (d_i \le p_m) $
其次,对于 \(a_i>0\) 的车,已知,其速度单调增加,只要其在最后一个监控被认为是超速的,其即为超速车辆。
运用公式3可得:
\({v_t}^2={v_i}^2+2a_i\cdot(p_m-d_i)\)
注意到:公式中的 \(v\) 的次数,全部为 \(2\) ,而使用浮点数可能有精度丢失,所以我们把所有 \(v\) 直接平方。
所以:
\(is_i = ({v_i}^2+2a_i\cdot(p_m-d_i)>V^2) \wedge (d_i \le p_m)\)
最后,对于 \(a_i<0\) 的车,其速度单调下降,所以,我们判断其在其遇到的第一个监控是否超速即可。
记该监控为 \(t\) ,则有:
\(is_i=({v_i}^2+2a_i\cdot(p_t-d_i)>V^2)\wedge(t\le m)\)
由此,第一问解决完毕。
第二问
不难发现:
一辆车被认为超速的监控编号是连续的。所以第二问被转化为坐标轴上的 \(n\) 条线段,最少用多少个点可以覆盖这些线段。
如样例一( \(O\) 为被监控拍到超速;\(-\)为未超速):
\(p_1=2\) | \(p_2=5\) | \(p_3=8\) | \(p_4=9\) | \(p_5=15\) | |
---|---|---|---|---|---|
1 | \(-\) | \(-\) | \(-\) | \(-\) | \(-\) |
2 | \(O\) | ||||
3 | \(O\) | \(O\) | \(O\) | \(O\) | |
4 | \(O\) | \(O\) | |||
5 | \(-\) | \(-\) | \(-\) | \(-\) | \(-\) |
这个问题可以通过贪心求解:
把所有线段按照右端点排序,遍历所有线段,若一个线段没有被排序,则使用其右端点进行覆盖。
代码
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+50;
int T,n,m,L,V;
int d[N],v[N],a[N],p[N],ans1,ans2;
struct node{
int l,r;
bool operator <(const node t)const{return r<t.r;}
}q[N];
bool is[N];//这个数组其实没必要写,不过也不差这点空间
void solve(){
cin>>n>>m>>L>>V;
V=V*V;
ans2=m;
ans1=0;//多测要清零
for(int i=1;i<=n;++i){
cin>>d[i]>>v[i]>>a[i];
v[i]=v[i]*v[i];
}
for(int i=1;i<=m;++i)cin>>p[i];
for(int i=1;i<=n;++i){
if(a[i]>=0){//a=0时,下式退化为v[i]>V
is[i]=(v[i]+2*a[i]*(p[m]-d[i])>V)&(d[i]<=p[m]);
if(a[i]==0)
q[i].l=is[i]?lower_bound(p+1,p+1+m,d[i])-p:0;
else
q[i].l=is[i]?lower_bound(p+1,p+1+m,max(0,(int)ceil((V-v[i])/2.0/a[i]))+d[i])-p:0;//速度加到V处的坐标
q[i].r=is[i]?m:0;
}
else{
int t=lower_bound(p+1,p+1+m,d[i])-p;//第一个遇见的监控
is[i]=(v[i]+2*a[i]*(p[t]-d[i])>V)&(t<=m);
q[i].l=is[i]?t:0;
q[i].r=is[i]?lower_bound(p+1,p+1+m,(int)ceil((V-v[i])/2.0/a[i])+d[i])-p-1:0;//速度减到V处的坐标
}
ans1+=is[i];//统计第一问的答案
}
sort(q+1,q+1+n);
for(int i=1,lst=0;i<=n;++i){//统计第二问的答案
if(q[i].l>lst){
lst=q[i].r;--ans2;
}
}
cout<<ans1<<" "<<ans2<<endl;
}
int main(){
cin>>T;
while(T--)solve();
return 0;
}
结尾
注意一些细节。
-
\(v_t>V\) 才为超速,\(v_t = V\) 不算
-
lower_bound和upper_bound的区别,以及末尾是否 \(-1\)
-
多测要清空