题解:P11232 [CSP-S 2024] 超速检测(民间数据)
upd:添加更多的补充说明。
upd:删除文章无意义内容。
智慧题。
Solution
直接枚举关掉哪些开哪些显然不可取,考虑预处理出每辆车超速区间。
由于题目中明确指出只需要考虑从南向北的车辆,也就是说初速度
注:本篇文章主要只用到了
具体地:
-
若
a_i=0 ,直接判断是否超速即可(从上道路的位置到道路末端)。 -
若
a_i>0 ,分两种情况,一种是初速度已经超了,一种是初速度还没超需要你算的。根据运动学知识,
\Delta v = a\times \Delta t ,再利用位移公式x=v_{0}\times t + \dfrac{1}{2}\times a \times t^2 易求,\Delta v 和a 是已知的,弄出\Delta t 代入第二个式子就可以得到位移x 。 -
若
a_i<0 ,同上,只是注意若初速度大于题目中所给V 较为复杂,可以采用物理中称为逆向思想的方法,即假设速度为0 ,反推当速度加到v_i 和V 时的位移,二者作差加上d_i 即可求出区间。
注意,有些区间里可能没有监控,判断一个区间内是否有点可以采用二分实现。具体地,二分找到第一个在区间左端点右方的监控,判断此监控是否包含在区间内即可。
第一步完成,还有第二步。
现在题目转化为了给你
这就是典型的贪心模型。考虑按右端点从小到大排序,每次贪心的选尽量靠右端点的监控,具体操作可以通过二分实现。
以上便是该题目的两步操作。
注意到开区间的存在,我们可以考虑给开区间加上或减去一个极小浮点值
时间复杂度
code
upd:更改代码为考场代码。
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define inf 0x3fffffff
using namespace std;
const int N = 1e5 + 5;
const double eps = 1e-8;
struct Cars {
double d, v, a;
#define d(p) b[p].d
#define v(p) b[p].v
#define a(p) b[p].a
} b[N];
struct node {
double l, r;
#define l(p) c[p].l
#define r(p) c[p].r
bool operator < (const node &a) const {return r == a.r ? l < a.l : r < a.r;}
} c[N];
int n, m, res;
double L, V, p[N];
void init() {
res = 0;
// cin >> n >> m >> L >> V;
scanf("%d %d %lf %lf", &n, &m, &L, &V);
rep(i, 1, n) scanf("%lf %lf %lf", &d(i), &v(i), &a(i)), l(i) = r(i) = -1;
rep(i, 1, m) scanf("%lf", &p[i]);
}
void sol() {
init();
rep(i, 1, n) {
if(a(i) == 0) {
// cout << i << '\n';
if(v(i) > V) l(i) = d(i), r(i) = L;
}
else if(a(i) > 0) {
if(v(i) > V) l(i) = d(i), r(i) = L;
else {
double lt = V - v(i);
double t = lt / a(i);
double sum = v(i) * t + 0.5 * a(i) * t * t;
l(i) = d(i) + sum + eps, r(i) = L;
}
}
else if(a(i) < 0) {
if(v(i) > V) {
// cout << i << ' ';
a(i) = fabs(a(i));
double lmt = v(i) / a(i);
double lms = 0.5 * a(i) * lmt * lmt;
double lt = V;
double t = lt / a(i);
double sum = 0.5 * a(i) * t * t;
l(i) = d(i), r(i) = d(i) + (lms - sum) - eps;
}
}
}
// cerr<<1;
// return;
// exit(0);
int tot = n; n = 0;
rep(i, 1, tot) {//检查区间内是否有监控(first question)
// printf("%.3lf %.3lf\n", d(i), r(i));
if(l(i) > r(i) || (l(i) == r(i) && r(i) == -1)) continue;
int id = lower_bound(p + 1, p + 1 + m, l(i)) - p;
if(id == m + 1) continue;
if(p[id] > r(i)) continue;
++n;
l(n) = l(i), r(n) = r(i); r(n) = min(r(n), L);
}
// cout << n << ' ';
printf("%d ", n);
if(n == 0) return printf("%d\n", m), void();
sort(c + 1, c + 1 + n);
// rep(i, 1, n) printf("%.3lf %.3lf\n", l(i), r(i));
int id = upper_bound(p + 1, p + 1 + m, r(1)) - p - 1;
double pos = p[id];//开始贪心选点(second question)
// cout << pos << '\n';
rep(i, 2, n) {
if(l(i) <= pos && pos <= r(i)) continue;
else {
id = upper_bound(p + 1, p + 1 + m, r(i)) - p - 1;
pos = p[id];
// cout << i << ' ' << pos << '\n';
++res;
}
}
printf("%d\n", m - res - 1);
// cout << m - res - 1;
// cout << '\n';
// exit(0);
}
signed main() {
// FASTIO;
// freopen("detect.in", "r", stdin);
// freopen("detect.out", "w", stdout);
int _; cin >> _;
while(_--) sol();
return 0;
}
后记
考场上因为大量输入 double 本地 TLE 了调了很久,cin 输入 double 会非常慢,正确的做法应该是输入 int 在计算时转为 double,将 cin 换为 scanf 读入在本地会快一点,但是实测在各大 OJ 包括 CCF 机子上用 cin 都可以过,可以看我提交记录。