原題傳送門
考場上我先是一個暴力dp
表示前個數最後一組有個的答案
枚舉就行了
發現有冗餘轉移,可以把省掉
達到,此方法就已經到頭了,因爲狀態就是
我的一個基友在考場上5min秒掉,而且狀態是一維的,但他當時無法證明正確性
可以發現我的上一個方法中一定比優
換句話說,以確定某個數結尾後,合理的情況下,最後一組的數越少(最後一組總和越小)越好
證明:
所以對於每個,只需要記錄最優解下,這最後一組有幾個(上一組結尾下標)與dp值就行了
令表示最優解下以結尾上一組結尾下標
表示dp數組
若
則
這也是的
那麼既然狀態優化成了,接下來就可以考慮優化時間了
對於每個,一定是越大越好,所以其實本題根本不用dp,因爲最優解的計算是確定的,就是找到最大的,使得
移項,,左邊是遞增的,所以單調隊列維護即可求得答案
以上解決了88pts,剩下12pts,顯然需要高精,然而,我用了__int128
Code:
#include <bits/stdc++.h>
#define maxn 40000010
#define maxm 100010
#define LL long long
using namespace std;
const LL qy = 1 << 30;
int n, type, pre[maxn], h, t, q[maxn];
LL sum[maxn], b[maxn], p[maxm], l[maxm], r[maxm], m;
__int128 ans;
inline LL read(){
LL s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
void write(__int128 x){
if (!x) return;
if (x < 0) putchar('-'), x = -x;
write(x / 10), putchar(x % 10 + '0');
}
LL calc(int x){ return (sum[x] << 1) - sum[pre[x]]; }
int main(){
n = read(), type = read();
if (type == 1){
LL x = read(), y = read(), z = read();
b[1] = read(), b[2] = read();
LL m = read();
for (int i = 1; i <= m; ++i) p[i] = read(), l[i] = read(), r[i] = read();
for (int i = 3; i <= n; ++i) b[i] = (x * b[i - 1] + y * b[i - 2] + z) % qy;
for (int i = 1; i <= m; ++i)
for (int j = p[i - 1] + 1; j <= p[i]; ++j) sum[j] = sum[j - 1] + b[j] % (r[i] - l[i] + 1) + l[i];
} else
for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + read();
q[++t] = 1;
for (int i = 2; i <= n; ++i){
while (h < t && sum[i] >= calc(q[h + 1])) ++h;
pre[i] = q[h];
while (h < t && calc(i) <= calc(q[t])) --t;
q[++t] = i;
}
while (n) ans += (__int128)(sum[n] - sum[pre[n]]) * (sum[n] - sum[pre[n]]), n = pre[n];
write(ans);
return 0;
}