划分
首先大家应该都能想到一个朴素的 dp: \(f_i, j\) 表示最后一段是 \([j, i]\) 时最小的代价,转移显然,时间复杂度 \(\mathcal{O}(n^3)\), 期望得分 \(36\)。
然后考虑优化,发现 dp 没啥用数据结构优化的前途,但是这题是 dp,所以大概率需要我们发现一些题目的性质,得到贪心的方法,从而省去不优的状态。
有如下两个观察:
-
一段和为 \(a + b\) 的数如果切割成一段和为 \(a\) 的与一段和为 \(b\) 的仍满足答案,则由于 \((a + b)^2 > a^2 + b^2\), 且 \(b < a + b\), 不会使后续划分变差,所以这一定是不劣的。
-
如果一段数划分为了一段和为 \(a\) 的以及一段和为 \(b + x\) 的,且 \(a + x \le b\), 我们可以取出 \(b\) 中一段和为 \(x\) 的放入 \(a\) 中,根据 \(a \le b\) 得到 \((a + x) ^ 2 + b^2 < a + (b + x)^2\), 这样调整一定更优。
综合以上观察我们发现:每个 \(i\) 选择的决策点 \(j\)(即划分出 \([j, i]\))一定是最大的满足条件的 \(j\), 因为如果决策点是 \(k\) 且 \(k < j\), 那么完全可以将 \([k + 1, j]\) 这段放在前面而不是划分到 \(i\) 这一段,而放在前面的情况已经在 \(j\) 处考虑过了。如果决策点是 \(k\) 且 \(k > j\), 那么根据观察一多划分肯定更优。
由此,我们设计出了一个 \(\mathcal{O}(n^2)\) 的 dp,记决策点为 \(las_i\), 前缀和数组为 \(s\), 转移为
如何优化?对于上面的条件,移项得到
记 \(val_j = 2s_j - s_{las_j}\), 我们发现如果 \(k < j\) 且 \(val_j \le s_i \lor val_j \le val_k\), \(k\) 就一定不如 \(j\) 优。
这个过程想到了什么?单调队列。只不过是把弹出队首的条件改了一下而已。
最后的统计答案就每次跳 \(las\) 即可。
时间复杂度 \(\mathcal{O}(n)\)。
下面是一些细节:
- 注意开
__int128 - 这题卡空间,
long long类型的数组能且只能开一个,这里只开前缀和数组,将 \(val\) 数组用前缀和 \(\mathcal{O}(1)\) 计算即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 4e7 + 10, M = 1e5 + 10, mod = (1 << 30) - 1;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
#ifdef ONLINE_JUDGE
return ;
#endif
cout << arg << ' ';
dbg(args...);
}
int n, typ, a[N], las[N], b[N], m, p[M], q[N];
ll x, y, z, s[N];
ll calc(int i) { return (s[i] << 1) - s[las[i]]; }
int stk[60], top;
void print(i128 num) {
while (num) { stk[++top] = num % 10; num /= 10; }
while (top) cout << stk[top--];
cout << '\n';
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> typ;
if (!typ) {
for (int i = 1; i <= n; i++) cin >> a[i];
} else {
cin >> x >> y >> z >> b[1] >> b[2] >> m;
for (int i = 3; i <= n; i++) b[i] = (x * b[i - 1] + y * b[i - 2] + z) & mod;
for (int i = 1, l, r; i <= m; i++) {
cin >> p[i] >> l >> r;
for (int j = p[i - 1] + 1; j <= p[i]; j++) {
a[j] = b[j] % (r - l + 1) + l;
}
}
}
s[0] = 0;
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
int fr = 1, re = 0; q[++re] = 0;
for (int i = 1; i <= n; i++) {
while (fr < re && calc(q[fr + 1]) <= s[i]) fr++;
las[i] = q[fr];
while (fr <= re && calc(q[re]) >= calc(i)) re--;
q[++re] = i;
}
i128 ans = 0; int pos = n;
while (pos) { i128 num = s[pos] - s[las[pos]]; num = num * num; ans += num; pos = las[pos]; }
print(ans);
return 0;
}

浙公网安备 33010602011771号