P10769 「CROI · R2」公交接驳
Genius_Star · · 题解
或许更好的阅读体验。
思路:
题意比较复杂。
我们称一个班车管辖一个换乘站当且仅当这个班车是最先到达这个换乘站(或者同时到达且价值更小),称其实际管辖当且仅当到达的时间大于等于列车到换乘站的时间。
然后容易发现对于两个班车,如果一个班车在某一个换乘站上比另外一个更早到达,那么之后一定是这个班车领先;所以每个班车管辖的区域一定是以其始发站开头的前缀(或者压根没有,这没必要考虑)。
然后考虑最重要的性质保证两个换乘站之间乘坐公交车花费的时间一定不小于乘坐市郊铁路花费的时间;这说明每个班车实际管辖的范围一定是一个区间,即可能我一个前缀中的站我先到了但是列车还没到。
考虑一个被实际管辖的极长区间
那么对于这样一个序列
-
考虑
[l, r] 的上一个区间[l', l - 1] ,如果l' \ge p :那么[l', l - 1] 的班车也是在p 处时刻t_{l'} - \operatorname{dis}(p, l') 发车的,根据之前的性质t_l - \operatorname{dis}(p, l) \ge t_{l'} - \operatorname{dis}(p, l') ,即[l, r] 的班车先发车,考虑其到l' 的时刻t_l - \operatorname{dis}(p, l) + \operatorname{dis}(p, l') = t_l -\operatorname{dis}(l, l') \le t_{l'} ,那么到l' 时列车还没开到(或者恰好一起走了不劣),得证。 -
否则
l' < p :首先需要证明p 处发车时刻小于等于[l', l - 1] 的车到p 的时刻即可,即t_l - \operatorname{dis}(p, l) \le t_{l'} + \operatorname{dis}(l', p) ,即t_l - t_{l'} \le \operatorname{dis}(p, l) + \operatorname{dis}(l', p) = \operatorname{dis}(l', l) ,这是题目给的性质;还要证明[l', l - 1] 的车到l 时\ge t_l ,这也是那个性质,于是得证。
上面还需要讨论价值相同比较到达时间的情况,但是较为简单,就不说了。
于是可以令
令
于是预处理
打表
证明一下:
考虑证明
w(i, j) + w(i + 1, j + 1) \le w(i, j + 1) + w(i + 1, j) ,抵消一下等价于v_{i + 1} (S_j - S_i + t_{i + 1} - t_{j + 1})\le v_i (S_j - S_{i - 1} + t_i - t_{j + 1}) = v_i(S_j - S_i + t_i - t_{j + 1} + s_i) ,先消掉一些于是v_{i + 1}t_{i + 1} \le v_i(t_i + s_i) ,而t_{i + 1} \le t_i + s_i 是显然满足的,于是得证。
完整代码:
#include<bits/stdc++.h>
#define ls(k) k << 1
#define rs(k) k << 1 | 1
#define lowbit(x) x & (-x)
#define fi first
#define se second
#define popcnt(x) __builtin_popcount(x)
#define open(s1, s2) freopen(s1, "r", stdin), freopen(s2, "w", stdout);
using namespace std;
typedef __int128 __;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const int N = 1010;
inline ll read(){
ll x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')
f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
inline void write(ll x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int T, n, q, k;
int s[N], v[N], t[N];
ll W[N][N], dp[N][N];
inline void init(){
for(int i = 1; i <= n; ++i)
for(int j = i; j <= n; ++j)
W[i][j] = W[i][j - 1] + 1ll * v[i] * (s[j - 1] - s[i - 1] + t[i] - t[j]);
}
inline ll getw(int l, int r){
return W[l][r];
}
inline void solve(int l, int r, int kl, int kr, int x){
if(l > r || kl > kr)
return ;
int mid = (l + r) >> 1, kmid = -1;
for(int i = kl; i <= min(mid - 1, kr); ++i){
if(kmid == -1 || dp[x - 1][i] + getw(i + 1, mid) < dp[x][mid]){
dp[x][mid] = dp[x - 1][i] + getw(i + 1, mid);
kmid = i;
}
}
solve(l, mid - 1, kl, kmid, x);
solve(mid + 1, r, kmid, kr, x);
}
inline void solve(){
for(int i = 1; i <= n; ++i)
t[i] = read();
init();
for(int i = 1; i <= n; ++i)
dp[1][i] = getw(1, i);
for(int x = 2; x < n; ++x)
solve(1, n, 0, n, x);
q = read();
while(q--){
k = read();
if(k >= n)
putchar('0');
else
write(dp[k][n]);
putchar(' ');
}
putchar('\n');
}
int main(){
n = read();
for(int i = 1; i < n; ++i)
s[i] = read() + s[i - 1];
for(int i = 1; i <= n; ++i){
v[i] = read();
if(i > 1)
v[i] = min(v[i], v[i - 1]);
}
T = read();
while(T--)
solve();
return 0;
}
``