2020-2021 ACM-ICPC, Asia Seoul Regional Contest
2020-2021 ACM-ICPC, Asia Seoul Regional Contest
D. Electric Vehicle
- \(f[t][pos][r]\) 表示施展 \(t\) 次后,在 \(pos\),剩 \(r\) 的油最小耗费。
- key 状态:在充电站上,油空,油满。
- 一个关键的观察是:存在最优方案,不会在路径上连续两个充电站,不到 key 状态。
- 因为:可以在便宜的那多充点,调整方案。
- 只用关键状态转移即可。
f[t][pos][0]
=>
f[t+1][pos][w]
f[t][pos][0]
=>
f[t+1][pos'][0]
f[t][pos][w]
=>
f[t+1][pos'][0]
- 其中第 3 种转移,需要经过一个中间点,我们可以对于 \(\forall x,y\) 预处理在 \(x\) 满油,在中间点加油,最终到达 \(y\) 最小代价。这里需要用到距离性质剪掉一些无效枚举来优化。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1002;
const int inf = 2e9 + 7;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; cin >> n;
vector<int> a(n),b(n),c(n);
for (int i = 0; i < n; i ++) cin >> a[i] >> b[i] >> c[i];
int w, det;
cin >> w >> det;
int s = 0, t = 1;
vector<int> near[N];
int d[N][N], d2[N][N];
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
d[i][j] = abs(a[i] - a[j]) + abs(b[i] - b[j]);
if (d[i][j] <= w) near[i].push_back(j);
d2[i][j] = inf;
}
}
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) if (d[i][j] > w && d[i][j] < 2 * w) {
for (auto k: near[i]) {
if (d[k][j] <= w) {
d2[i][j] = min(d2[i][j], c[k] * (d[j][k] - (w - d[i][k])));
}
}
}
}
/*
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
printf("d[%d][%d] = %lld, d2[%d][%d] = %lld\n", i,j,d[i][j],i,j,d2[i][j]);
}
}
*/
const LL dpinf = 1e12;
LL down[12][N], up[12][N];
for (int i = 0; i < 12; i ++) for (int j = 0; j < N; j ++)
down[i][j] = up[i][j] = dpinf;
auto upd = [](LL &x, LL y) {
x = min(x, y);
};
down[0][s] = 0;
for (int i = 0; i <= det; i ++) {
for (int x = 0; x < n; x ++) {
//printf("down[%d][%d] = %lld, up[%d][%d] = %lld\n",i,x,down[i][x],i,x,up[i][x] );
if (down[i][x] < dpinf) {
upd(up[i+1][x], down[i][x] + w * c[x]);
}
for (int y = 0; y < n; y ++) {
if (d[x][y] <= w) {
upd(down[i+1][y], down[i][x] + d[x][y] * c[x]);
upd(up[i+1][y], up[i][x] + d[x][y] * c[y]);
}
if (d2[x][y] != inf) {
upd(down[i+1][y], up[i][x] + d2[x][y]);
}
}
}
}
LL ans = dpinf;
for (int i = 0; i <= det; i ++)
upd(ans, down[i][t]);
if (ans == dpinf) ans = -1;
cout << ans << endl;
}
L. Two Buildings
决策单调性:\(p(j)\) 表示 \(j\) 为右端点,最优的左端点位置,这个函数是不降的。
分治即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000000 + 10;
int a[N];
LL dp[N], ans = 0;
vector<int> vi, vj;
void solve(int l,int r,int L,int R) {
int mid = (l+r)>>1;
if (l > r) return;
LL mx = 0; int pos = -1;
for (int i = L; i <= R; i ++) {
if (vi[i] >= vj[mid]) break;
LL tmp = 1LL * (a[vi[i]] + a[vj[mid]]) * (vj[mid] - vi[i]);
if (tmp >= mx) {
pos = i, mx = tmp;
}
}
if (pos == -1) return;
ans = max(ans, mx);
solve(l, mid - 1, L, pos);
solve(mid + 1, r, pos, R);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; cin >> n;
for (int i = 0; i <= n; i ++) a[i] = dp[i] = 0;
for (int i = 1; i <= n; i ++) cin >> a[i];
int mx = 0;
for (int i = 1; i <= n; i ++) {
if (a[i] > mx) {
mx = a[i]; vi.push_back(i);
}
}
mx = 0;
for (int i = n; i >= 1; i --) {
if (a[i] > mx) {
mx = a[i]; vj.push_back(i);
}
}
reverse(vj.begin(), vj.end());
solve(0, (int)vj.size() - 1, 0, (int)vi.size() - 1);
cout << ans << endl;
}