题面
解法
从来没写过这样的……
- 第一问非常简单,能够从 j j j转移到 i i i的条件显然为 a [ i ] − a [ j ] ≥ i − j a[i]-a[j]≥i-j a[i]−a[j]≥i−j,移项可得 a [ i ] − i ≥ a [ j ] − j a[i]-i≥a[j]-j a[i]−i≥a[j]−j。不妨令 x [ i ] = a [ i ] − i x[i]=a[i]-i x[i]=a[i]−i,那么在 O ( n log n ) O(n\log n) O(nlogn)的复杂度内做最长不下降子序列就可以了。
- 对于第二问,假设 g [ i ] g[i] g[i]为保留第 i i i个房子的最小花费,那么转移比较显然: g [ i ] = m i n { g [ j ] + ( 2 ∗ a [ j ] + i − j ) ( i − j − 1 ) 2 } + a [ i ] + b [ i ] g[i]=min\{g[j]+\frac{(2*a[j]+i-j)(i-j-1)}{2}\}+a[i]+b[i] g[i]=min{g[j]+2(2∗a[j]+i−j)(i−j−1)}+a[i]+b[i]。把式子稍作化简一下可以得到 g [ i ] = m i n { g [ j ] − ( j + 1 ) a [ j ] + j ( j + 1 ) 2 + i ∗ x [ j ] } + a [ i ] + b [ i ] + i ( i − 1 ) 2 g[i]=min\{g[j]-(j+1)a[j]+\frac{j(j+1)}{2}+i*x[j]\}+a[i]+b[i]+\frac{i(i-1)}{2} g[i]=min{g[j]−(j+1)a[j]+2j(j+1)+i∗x[j]}+a[i]+b[i]+2i(i−1)。
- 当然,能够从 j j j转移到 i i i的条件有三个:1. f [ j ] + 1 = f [ i ] f[j]+1=f[i] f[j]+1=f[i]; 2. x [ j ] ≤ x [ i ] x[j]≤x[i] x[j]≤x[i]; 3. j < i j<i j<i。
- 简便起见,我们令 y [ i ] = g [ i ] − ( j + 1 ) a [ i ] + i ( i + 1 ) 2 y[i]=g[i]-(j+1)a[i]+\frac{i(i+1)}{2} y[i]=g[i]−(j+1)a[i]+2i(i+1)。
- 假设现在有两个决策点 j , k j,k j,k,并且 j j j更优,那么可以得到 y [ j ] + i ∗ x [ j ] < y [ k ] + i ∗ x [ k ] y[j]+i*x[j]<y[k]+i*x[k] y[j]+i∗x[j]<y[k]+i∗x[k],移项就可以变成 y [ j ] − y [ k ] < i ( x [ k ] − x [ j ] ) y[j]-y[k]<i(x[k]-x[j]) y[j]−y[k]<i(x[k]−x[j])。因为不知道 x [ j ] , x [ k ] x[j],x[k] x[j],x[k]的大小关系,我们不妨强制认为 x [ k ] < x [ j ] x[k]<x[j] x[k]<x[j],那么就可以得到 y [ j ] − y [ k ] x [ k ] − x [ j ] > i \frac{y[j]-y[k]}{x[k]-x[j]}>i x[k]−x[j]y[j]−y[k]>i的关系。
- 然后发现关系就变成了一个类似于斜率的式子,可以考虑斜率优化。
- 可以将每一个决策点用 ( − x [ i ] , y [ i ] ) (-x[i],y[i]) (−x[i],y[i])的方式扔进坐标系内,显然,我们要得到的是一个下凸壳,那么类似于NOI2007货币兑换的做法,可以用cdq分治来维护这样一个下凸壳。
- 然后考虑如何满足下标的几个限制。首先,我们可以分层进行dp,就是用 f [ i ] = t f[i]=t f[i]=t的点来更新 f [ i ] = t + 1 f[i]=t+1 f[i]=t+1的点。对于 x [ j ] ≤ x [ i ] x[j]≤x[i] x[j]≤x[i]和 j < i j<i j<i的限制在cdq分治中即可实现。
- 在查询的时候直接在下凸壳上二分就可以了。
- 时间复杂度: O ( n log 2 n ) O(n\log^2 n) O(nlog2n)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 100010; const ll inf = 1ll << 60;
int n, top, a[N], b[N], f[N], st[N]; ll g[N], x[N], y[N];
vector <int> v[N];
struct Node {
int fl, id;
bool operator < (const Node &q) const {return id < q.id;}
} q[N * 2];
bool cmp(int i, int j) {return (x[i] == x[j]) ? y[i] > y[j] : x[i] < x[j];}
double slope(int i, int j) {return 1.0 * (y[i] - y[j]) / (x[j] - x[i]);}
int calcf() {
int len = 0;
for (int i = 1; i <= n; i++) {
if (x[i] < 0) {f[i] = n + 1; continue;}
int l = 1, r = len, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (st[mid] <= x[i]) ans = mid, l = mid + 1;
else r = mid - 1;
}
f[i] = ans + 1;
if (ans == len) st[++len] = x[i]; else st[ans + 1] = x[i];
}
return len;
}
void query(int i) {
if (!top) return;
int l = 1, r = top - 1, j = st[top];
while (l <= r) {
int mid = (l + r) >> 1;
if (slope(st[mid], st[mid + 1]) < i) j = st[mid], r = mid - 1;
else l = mid + 1;
}
chkmin(g[i], g[j] + 1ll * (2 * a[j] + i - j) * (i - j - 1) / 2 + a[i] + b[i]);
}
void solve(int l, int r) {
if (l == r) return;
static int l1, l2, L[N], R[N];
int mid = (l + r) >> 1;
solve(l, mid), solve(mid + 1, r); top = l1 = l2 = 0;
for (int i = l; i <= mid; i++) if (!q[i].fl) L[++l1] = q[i].id;
for (int i = mid + 1; i <= r; i++) if (q[i].fl) R[++l2] = q[i].id;
if (!l1 || !l2) return;
sort(L + 1, L + l1 + 1, cmp), sort(R + 1, R + l2 + 1, cmp);
for (int i = 1, j = 1; i <= l2; i++) {
while (j <= l1 && x[L[j]] <= x[R[i]]) {
if (top && x[st[top]] == x[L[j]]) top--;
while (top >= 2 && slope(st[top - 1], st[top]) > slope(st[top], L[j])) top--;
st[++top] = L[j++];
}
query(R[i]);
}
}
int main() {
read(n);
for (int i = 1; i <= n; i++) read(a[i]), x[i] = a[i] - i;
for (int i = 1; i <= n; i++) read(b[i]), g[i] = inf;
int tx = calcf(); cout << tx << ' ';
for (int i = 0; i <= n; i++) v[f[i]].push_back(i);
for (int t = 0; t < tx; t++) {
int len = 0;
for (int i = 0; i < v[t].size(); i++) q[++len] = (Node) {0, v[t][i]};
for (int i = 0; i < v[t + 1].size(); i++) q[++len] = (Node) {1, v[t + 1][i]};
sort(q + 1, q + len + 1);
solve(1, len);
for (int i = 0; i < v[t + 1].size(); i++) {
int j = v[t + 1][i];
y[j] = -1ll * (j + 1) * a[j] + g[j] + 1ll * j * (j + 1) / 2;
}
}
ll ans = inf;
for (int i = 1; i <= n; i++)
if (f[i] == tx) chkmin(ans, g[i] + 1ll * (2 * a[i] + n - i + 1) * (n - i) / 2);
cout << ans << "\n";
return 0;
}