2 条题解
-
0
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 35e3 + 10; int a[N], b[N]; int mn_end[N], len; // [i]:长度为 i 的最长不降子序列(LIS)的最小结尾 int L[N]; // [i]:以 i 点为结尾 LIS 的长度 vector<int> G[N]; // LIS 长度桶 LL sumi[N], sumj[N]; // [k]:i 到 k 全部改成 b[i] / b[j] 的代价 LL dp[N]; // [i]:从虚拟起点到合法点 i 的最小总代价 int main () { ios::sync_with_stdio(false); cin.tie(0); int n; cin >> n; for (int i = 1; i <= n; i ++) { cin >> a[i]; b[i] = a[i] - i; } b[n + 1] = 1e9; // 不一定所有最长不降子序列(LIS)的结尾都是 n // 为了所有非法段都能被处理到,加入一个所有 LIS 的结尾点 memset(mn_end, 0, sizeof(mn_end)); int len = 0; // mn_end 数组的当前长度 for (int i = 1; i <= n + 1; i ++) { int l = 0, r = len, p = 0; while (l <= r) { int mid = (l + r) >> 1; if (mn_end[mid] <= b[i]) { p = mid; l = mid + 1; } else { r = mid - 1; } } if (p == len) { len ++; // 更新全局 LIS 长度 } mn_end[p + 1] = b[i]; // 长度为 p + 1 的 LIS 结尾最小值更新 // 如果 p + 2 也因为 i 可以被更新的更小,导致更新错误怎么办? // 答:不用担心 // 假设后面有一个点 x,me[p + 1](new) < x < me[p + 2](new) // 并且 me[p + 2](old) < x < me[p + 3](old) // 这样会导致本应更新 p + 2 的 x 更新了 p + 3 // 但很明显,不可能做到同时 < me[p + 2](new) 且 > me[p + 2](old) // 所以不用担心会更新错误 L[i] = p + 1; G[L[i]].push_back(i); } cout << n - len + 1 << "\n"; // 不合法点的数量,因为多算了一个 n + 1 memset(dp, 0x7f, sizeof(dp)); // 初始化最大值 G[0].push_back(0); // 添加虚拟起点 b[0] = -1e9; dp[0] = 0; // 保证所有点都能接它后面 for (int j = 1; j <= n + 1; j ++) { for (int i : G[L[j] - 1]) if (i < j && b[i] <= b[j]){ sumi[i] = 0; for (int k = i + 1; k < j; k ++) { sumi[k] = sumi[k - 1] + abs(b[k] - b[i]); } sumj[j] = 0; for (int k = j - 1; k > i; k --) { sumj[k] = sumj[k + 1] + abs(b[j] - b[k]); } for (int k = i; k < j; k ++) { dp[j] = min(dp[j], dp[i] + sumi[k] + sumj[k + 1]); } } } cout << dp[n + 1] << "\n"; return 0; } -
0
- 1
信息
- ID
- 2702
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 20
- 已通过
- 12
- 上传者