P5665 [CSP-S2019] 划分
写在前面:
毒瘤题,卡空间。
$\mathbf{Part}$ $\mathbb{A}$ :$O(n^3)$
设 $s_n = \sum_{i=1}^{n} a_i$,枚举 $i,j,k$,可以得到 $O(n^3)$ 的做法:$$dp_{i,j} = \min_{s_i-s_j \ge s_j - s_k} \left \{ dp_{j,k} +(s_i-s_j)^2\right \}$$
$\mathbf{Part}$ $\mathbb{B}$ :$O(n^2)$
考虑最后一段,发现最优方案中最后一段一定最小。感性理解下:最后一段越小,相应前面的段也会更小,所以分的段会变多。根据 $(a+b)^2 \ge a^2+b^2$ ,分的段越多,值会越小。设 $d_i$ 为以 $i$ 结尾划分那一段的值,有:$$d_i = s_i - s_{j},j = \max_{s_i-s_k \ge d_k} k$$
$\mathbf{Part}$ $\mathbb{C}$ :$O(n)$
考虑单调队列优化上述过程,做到 $O(n)$
注意事项:节约空间
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e7 + 1, M = 1e5 + 1;
const int Mo = 1 << 30;
int n, T, t[N], q[N], h, tl, m, x, y, z;
vector<int>b, p, l, r;
vector<ll>f, s;
void write(__int128 x) {
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
int main(){
scanf("%d %d", &n, &T); s.resize(n + 1);
if(T == 1) {
b.resize(n + 1), p.resize(M), l.resize(M), r.resize(M);
scanf("%d %d %d %d %d %d", &x, &y, &z, &b[1], &b[2], &m);
for(int i = 1 ; i <= m ; i ++) scanf("%d %d %d", &p[i], &l[i], &r[i]);
for(int i = 3 ; i <= n ; i ++) b[i] = (0ll + 1ll * b[i - 1] * x + 1ll * b[i - 2] * y + z) % Mo ;
for(int i = 1 ; i <= m ; i ++)
for(int j = p[i - 1] + 1 ; j <= p[i] ; j ++) { s[j] = (b[j] % (r[i] - l[i] + 1)) + l[i] ; s[j] += s[j - 1]; }
b.resize(0), p.resize(0), l.resize(0), r.resize(0);
b.clear(), p.clear(), l.clear(), r.clear();
b.shrink_to_fit(), p.shrink_to_fit(), l.shrink_to_fit(), r.shrink_to_fit();
}
else {
for(int i = 1;i <= n;i++) scanf("%lld", &s[i]);
for(int i = 1;i <= n;i++) s[i] += s[i - 1];
}
f.resize(n + 1);
q[h = tl = 1] = 0;
for(int i = 1;i <= n;i++) {
while(h < tl && s[i] >= f[q[h + 1]] + s[q[h + 1]]) h++;
f[i] = s[i] - s[q[h]], t[i] = q[h];
while(h < tl && f[q[tl]] + s[q[tl]] >= f[i] + s[i]) tl--;
q[++tl] = i;
}
__int128 ans = 0;
for(int i = n;i ;i = t[i]) ans += (__int128)f[i] * f[i];
write(ans);
return 0;
}

浙公网安备 33010602011771号