题目笔记——P10224 [COCI 2023/2024 #3] Vrsar
一直以为是个黄……
诈骗题
本题中有一个点值得注意:直接到一座山丘绝对比中间又去了其他山丘的滑雪时长长,以下是证明:
从同一个点到终点山丘要走的路程相同(也就是所花时间相同),因为定义的最终山丘相同,所以滑雪时长相同,但是如果在中途去往其他山丘,不在滑雪的时间就多了一个下山时间,又因为总时间不变,所以直接到达一个山丘的滑雪时长一定比中途去过其他山丘的滑雪时长长。
暴力
证明了直接到一座山丘绝对比中间又去了其他山丘的滑雪时长长后,可以很容易想到每得到一个初始点后,除去从当前点到山丘之间要走的距离所花的时间后,剩下的就是滑雪到所要去的山丘的最长时间,复杂度 \(O(n\times m)\)。
TLE Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 20;
struct node{
int x, t, s;
}a[N];
int n, m;
bool cmp(node x, node y){return x.x < y.x;}
signed main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d%d%d", &a[i].x, &a[i].t, &a[i].s);
for(int i = 1, l, r, x, ans; i <= m; i++){
scanf("%lld", &x);
ans = LONG_LONG_MIN;
for(int i = 1; i <= n; i++){
ans = max(ans, a[i].t - abs(a[i].x - x));
}
printf("%lld ", ans);
}
return 0;
}
正解
因为初始位置有 \(m\) 个,所以只能在取答案最大值时优化。虽然初始位置不确定,但山丘开放截止时间不变,所以我们可以化简式子:
\[ans=\sum\limits_{i=1}^nt_i-|x_i-now|
\]
\[ans=\left\{\begin{aligned}
t_i-x_i+now (x_i>now) \\
t_i+x_i-now (x_i\le now)\\
\end{aligned}\right.\]
注:这里提到的 \(now\) 就是代码中的初始位置 \(x\)。
所以我们可以先将山丘以前后顺序排列,再预处理出前缀的 \(t_i-x_i\) 和后缀的 \(t_i+x_i\),最后对于每一个 \(now\) 进行二分找到从后往前的第一个大于 \(now\) 的 \(x\),最后与 \(0\) 取最大值就行了。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 20;
struct node{
int x, t, s;
}a[N];
int n, m, sm[N], bi[N];
bool cmp(node x, node y){return x.x < y.x;}
signed main(){
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; i++) scanf("%lld%lld%lld", &a[i].x, &a[i].t, &a[i].s);
sort(a + 1, a + n + 1, cmp);
sm[0] = bi[n + 1] = -1e18;
for(int i = 1; i <= n; i++) sm[i] = max(sm[i - 1], a[i].t + a[i].x);
for(int i = n; i >= 1; i--) bi[i] = max(bi[i + 1], a[i].t - a[i].x);
for(int i = 1, l, r, x; i <= m; i++){
scanf("%lld", &x);
l = 0, r = n;
while(l < r){
int mid = ((l + r + 1) >> 1);
if(a[mid].x <= x) l = mid;
else r = mid - 1;
}
printf("%lld ", max(0ll, max(sm[l] - x, bi[l + 1] + x)));
}
return 0;
}

浙公网安备 33010602011771号