ABC201_F-Insertion Sort
题意:长度为 \(n(1\le n\le2\times10^5)\) 的序列,可以重复以下三种操作之一任意次任意顺序使得序列递增。
- 选择一个 \(i\),消耗 \(a_i\) 将第 \(i\) 个数移到任意位置
- 选择一个 \(i\),消耗 \(b_i\) 将第 \(i\) 个数移到最左端
- 选择一个 \(i\),消耗 \(c_i\) 将第 \(i\) 个数移到最右端
每个人至多移动一次,问题即每个人分配 4 个状态中的一个,\(dp[i]\):需要的最小花费使得小于等于 \(i\) 的序列有序。
\[dp[i]=\min(\sum_{j=1}^{i-1}\min(A_j,B_j),\min_{j\lt i,pos[j]\lt pos[i]}(dp[j]+\sum_{k=j+1}^{i-1}A_k))
\]
自然写法是 \(O(n^2)\),可以用树状数组和累加和优化到 \(O(n\log n)\),\(dp\) 处理完后,可以通过 \(\min_{1\le i\le N}(dp[i]+\sum_{j=i+1}^N\min(A_j,C_j))\) 获得答案。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 2e5 + 5;
int n;
int p[N], pos[N];
ll a[N], b[N], c[N], suma[N], sumb[N], sumc[N], dp[N], bit[N];
void update(int x, ll v) {
for (; x <= n; x += x & -x)
bit[x] = min(bit[x], v);
}
ll query(int x) {
ll res = 1e18;
for (; x; x -= x & -x)
res = min(res, bit[x]);
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &p[i]);
pos[p[i]] = i;
}
for (int i = 1; i <= n; ++i) scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
for (int i = 1; i <= n; ++i) {
suma[i] = suma[i-1] + a[i];
sumb[i] = sumb[i-1] + min(b[i], a[i]);
sumc[i] = sumc[i-1] + min(c[i], a[i]);
}
ll ans = 1e18;
for (int i = 1; i <= n; ++i) {
/* for (int j = 1; j < i; ++j) {
if (pos[j] < pos[i]) {
dp[i] = min(sumb[i-1], dp[j] + suma[i-1] - suma[j]);
}
} // from O(n^2) to O(nlogn) */
dp[i] = min(sumb[i-1], query(pos[i] - 1) + suma[i-1]);
update(pos[i], dp[i] - suma[i]);
ans = min(ans, dp[i] + sumc[n] - sumc[i]);
}
printf("%lld\n", ans);
return 0;
}
posted by 2inf

浙公网安备 33010602011771号