[luogu5665]划分

暴力dp,用f[i][j]表示前i个数,最后一个区间是(j,i]的最小答案,转移方程用可以用前缀和来优化,复杂度为$o(n^3)$
(然后可以各种优化到$o(n^2)$,但这不需要)
输出f[i][j],可以发现若f[i][j-1]和f[i][j]合法,那么$f[i][j]\le f[i][j-1]$
证明:数归,考虑令f[i][j]和f[i][j-1]都合法,设f[i][j-1]由f[j-1][k]转移,f[i][j]由f[i][k']转移,必然有$k\le k'$,即在左边可以删掉若干个数(可以为0个)来保证合法
按照这样的策略划分f[i][j-1],设划分出的区间和分别是$S1\le S2\le ……\le St$,然后分别在左边删除Li,右边加入Ri,显然有$L1=Rt=0$且$R_{i-1}=Li$,那么区间和从$\sum_{i=1}^{t}Si^{2}$变成$\sum_{i=1}^{t}(Si+Ri-Li)^2=\sum_{i=1}^{t}Si^2+\sum_{i=1}^{t}(Ri-R_{i-1})^{2}+2\sum_{i=1}^{t}Si(Ri-R_{i-1})\le \sum_{i=1}^{t}Si^2+2\sum_{i=1}^{t}Ri(Si-S_{i-1})\le \sum_{i=1}^{t}Si^2$
通过证明,同时也可以得到最优方案如何构造,用优先队列来维护出最后一个合法的,然后从n往前选择即可
考场上需要写高精度(比较懒就用__int128了)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod (1<<30)
 4 #define N 40000007
 5 #define ll long long
 6 #define lll __int128
 7 int n,type,m,x,y,z,q[N],f[N],b[N];
 8 ll ans,a[N];
 9 ll calc(int k){
10     return 2*a[k]-a[f[k]];
11 }
12 void write(lll k){
13     if (k>9)write(k/10);
14     putchar(k%10+'0');
15 }
16 int main(){
17     scanf("%d%d",&n,&type);
18     if (!type)
19         for(int i=1;i<=n;i++){
20             scanf("%d",&x);
21             a[i]=a[i-1]+x;
22         }
23     else{
24         scanf("%d%d%d%d%d%d",&x,&y,&z,&b[1],&b[2],&m);
25         for(int i=3;i<=n;i++)b[i]=(1LL*x*b[i-1]+1LL*y*b[i-2]+z)%mod;
26         int xx=1;
27         for(int i=1;i<=m;i++){
28             scanf("%d%d%d",&x,&y,&z);
29             for(int j=xx;j<=x;j++)a[j]=a[j-1]+b[j]%(z-y+1)+y;
30             xx=x+1;
31         }
32     }
33     x=1;
34     y=0;
35     for(int i=1;i<=n;i++){
36         while ((x<=y)&&(calc(q[x])<=a[i]))x++;
37         f[i]=q[x-1];
38         while ((x<=y)&&(calc(q[y])>=calc(i)))y--;
39         q[++y]=i;
40     }
41     lll ans=0,o=1;
42     for(int i=n;i;i=f[i])ans+=o*(a[i]-a[f[i]])*(a[i]-a[f[i]]);
43     write(ans);
44 }
View Code

 

posted @ 2019-11-20 22:03  PYWBKTDA  阅读(147)  评论(0编辑  收藏  举报