[CSP2019] 划分 解题报告

[CSP2019] 划分

题意

有一个长度为 (n) 的序列 (a_i) ((n le 4 imes 10^7)), 求

[(sum_{i=1}^{k_1} a_i)^2 + (sum_{i=k_1+1}^{k_2} a_i)^2 dots (sum_{i=k_{j-1}+1}^{n} a_i)^2 ]

的最小值.

思路

结论: 上述式子能取到最小值, 当且仅当最后一块 (即(sum_{i=k_{j-1}+1}^{n} a_i)) 取到了最小值.
证明: 暂时不会.

那我们就 (dp) 保证最后一块最小就行了.

(val[i]) 为以 (i) 结尾的序列的最后一块的最小值.

那么对于每一位 (i) 只需从 (i-1)(0) 找到第一位 (j) 满足 (sum[i] - sum[j] ge val[j]), (val[i]) 即为 (sum[i]-sum[j]). ((sum) 为前缀和数组)

可以发现, 每次 (j) 值是有单调性的, 所以可以考虑单调队列优化.

队头的筛选很简单, 满足要求就 +1, 找到最后一个满足条件的 (j) 即可.

再考虑队尾的处理, 如果只是单纯的把当前的 (i) 加入队列中的话, 就会出现 "队列中后面满足但前面不满足的情况",
比如说下面这个例子.

7 0
15 19 14 1 5 7 17

(lst[i])(i) 的上一个块的终点.

在上面这个例子中, (lst[6]=2), 那么 (i==7) 时, 队头 (t1) 就会在 (2) 的位置,
但是, 由于 (lst[3]=1, val[3]=33), 而 (sum[7]-sum[3]==30 < val[3]), 所以 (t1) 会停在 (3).

但是, (lst[5]=2), 所以 (lst[7]) 实际上是可以取到 (5) 的.

所以, 我们可以发现, 要在加入一个新的选项时, 要去除一些不优情况.

首先想到的是把队列中 (val[que[t2]] > val[i]) 的去掉, ( (t2) 为队尾 )
但是这样可能会导致后面的一些 (i) 取不到满足要求的 (j),

所以我们还要保证当 (que[t2]) 对于后面的某个数满足要求时, (i) 也一定满足要求.

(t) 为后面的一个点, 那么就要满足: (sum[t]-sum[que[t2]] ge val[que[t2]]) 可以推导到 (sum[t]-sum[i] ge val[i]).
即 $ val[que[t2]] + sum[que[t2]] ge val[i] + sum[i] $,

那么, 在把 (i) 加入队尾的时候判断上述条件, 若满足则 (t2--).

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+7;
int n,ty,que[N],t1=1,t2,pre[N];
ll a[N],sum[N],val[N],ans;
int main(){
  cin>>n>>ty;
  for(int i=1;i<=n;i++){
    scanf("%lld",&a[i]);
    sum[i]=sum[i-1]+a[i];
  }
  que[++t2]=0;
  for(int i=1;i<=n;i++){
    while(t1<t2&&sum[i]-sum[que[t1+1]]>=val[que[t1+1]]) t1++;
    pre[i]=que[t1];
    val[i]=sum[i]-sum[que[t1]];
    while(t2>=t1&&val[i]+sum[i]<=val[que[t2]]+sum[que[t2]]) t2--;
    que[++t2]=i;
  }
  int p=n;
  while(p){
    ans+=val[p]*val[p];
    p=pre[p];
  }
  printf("%lld
",ans);
  return 0;
}
原文地址:https://www.cnblogs.com/BruceW/p/12006359.html