斜率优化入门题题单$QwQ$
其实就是这一篇的那个例题帕的大部分题目的题解就写这儿辣,,,
因为都是些基础题不想专门给写题解,,,但是又掌握得差不得不写,,,
麻油办法就写一块儿好辣$QwQ$
当然辣比较难的我就没放进来辣$QwQ$
[X]玩具装箱
解析在上一篇就港辣_(:з」∠)_
懒得复制辣,就只放个代码了昂$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define lf double #define gc getchar() #define int long long #define mp make_pair #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) const int N=50000+10; int n,l,sum[N],stck[N],head=1,tail=1,f[N],A[N],B[N]; il int read() { ri x=0;rb y=1;rc ch=gc; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int sqr(ri x){return 1ll*x*x;} il lf X(ri x){return B[x];} il lf Y(ri x){return f[x]+sqr(B[x]);} il lf slope(ri i,ri j){return (Y(i)-Y(j))/(X(i)-X(j));} main() { freopen("3195.in","r",stdin);freopen("3195.out","w",stdout); n=read();l=read();rp(i,1,n)sum[i]=sum[i-1]+read(),A[i]=sum[i]+i,B[i]=sum[i]+i+l+1;B[0]=l+1; rp(i,1,n) { while(head<tail && slope(stck[head],stck[head+1])<2*A[i])++head;f[i]=f[stck[head]]+sqr(A[i]-B[stck[head]]); while(head<tail && slope(stck[tail-1],i)<slope(stck[tail-1],stck[tail]))--tail;stck[++tail]=i; } printf("%lld\n",f[n]); return 0; }
[X]防御准备
先考虑无优化的$dp$呗
设$f_{i}$表示第$i$个放路灯的最小花费,转移就$f_{i}=(f_{j}+\frac{(i-j+1)*(i-j)}{2}+)_{min}$
显然复杂度不对,于是考虑斜率优化.
先拆式子,$2\cdot f_{i}=(2\cdot f_{j}+i^{2}-2\cdot i\cdot j+j^{2}+i+j)_{min}+2\cdot a_{i}$
先把$min$搞掉,变形下就是$2\cdot f_{j}+j^{2}+j=2\cdot i\cdot j-i^{2}-2\cdot f_{i}-2\cdot a_{i}-i$
显然考虑设$2\cdot f_{j}+j^{2}+j=y,2\cdot i=k,j=x,b=-1-i^{2}+2\cdot f_{i}-2\cdot a_{i}$
然后求$min$就下凸壳昂,然后就做完辣,,,?$QwQ$
View Code#include<bits/stdc++.h> using namespace std; #define il inline #define lf double #define gc getchar() #define int long long #define mp make_pair #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) const int N=1e6+10,inf=1e9; int n,stck[N],head,tail,f[N],a[N]; il int read() { ri x=0;rb y=1;rc ch=gc; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int sqr(ri x){return 1ll*x*x;} il lf X(ri x){return (lf)x;} il lf Y(ri x){return (lf)2*f[x]+sqr(x)+x;} il lf slope(ri i,ri j){return (Y(i)-Y(j))/(X(i)-X(j));} signed main() { //freopen("3156.in","r",stdin);freopen("3156.out","w",stdout); n=read();rp(i,1,n)a[i]=read();//f[0]=inf;f[1]=a[1];stck[++tail]=1; rp(i,1,n) { while(head<tail && slope(stck[head],stck[head+1])<2*i)++head; ri j=stck[head];f[i]=f[j]+(i-j-1)*(1ll*i-j)/2+a[i]; while(head<tail && slope(stck[tail-1],i)<slope(stck[tail-1],stck[tail]))--tail;stck[++tail]=i; } printf("%lld\n",f[n]); return 0; }嗷想港个小$tip$
这样的,因为开始看到题的$gql$太$naive$了,并没有意识到左边右边其实是一样儿的,就想着,先翻转过来
然后翻转过来之后发现会麻烦一些(因为会有强制第一个选这种以及最后一个可以不选这种问题,,,
于是我就又换回正序辽,,,就想提醒下不要倒叙走,,,会麻烦很多我感觉$QAQ$
[ ]序列分割
开始看感觉长得很区间$dp$的样子,,,然后就显然无法优化,$O(n^{2})$50$pts$滚粗了嘤嘤嘤
这里要先证明一个性质,就说切割的顺序并不影响最终的结果
下证
显然只要证明切两刀时的顺序不影响结果就能用数学归纳法得切若干刀都不影响结果嘛
于是考虑证切两刀时的顺序不影响结果
设切两刀得到的三块分别为$a_{1},a_{2},a_{3}$
则对于先切开左边再切右边是$as=a_{1}\cdot (a_{2}+a_{3})+a_{2}\cdot a_{3}=a_{1}\cdot a_{2}+a_{2}\cdot a_{3}+a_{1}\cdot a_{3}$
然后对于先切开右边再切左边是$as=a_{3}\cdot (a_{2}+a_{1})+a_{2}\cdot a_{1}=a_{1}\cdot a_{2}+a_{2}\cdot a_{3}+a_{1}\cdot a_{3}$
得证!
欧克然后接下来就可以考虑用线性$dp$直接枚举在哪儿切就好嘛$QwQ$
设$f_{i,j}$表示在$i$的左处切了一刀,共切了$j$刀时的$max$,有$f_{i,j}=(f_{k,j-1}+\sum_{p=k+1}^{i-1}a_{p}\cdot \sum_{p=i}^{n}a_{p})_{max}$
这时候复杂度是$O(n^{2}\cdot k)$,显然过不去昂.考虑优化到$O(n\cdot k)$就欧克了嘛$QwQ$
先把$j$这一维省略,因为并不需要优化它,显然只要让它在最外层循环就不会打扰到内部的转移辣$QwQ$
于是转移式就再变形了下$QwQ$
$f_{i}=(f_{k}+(sum_{i-1}-sum_{k})\cdot (sum_{n}-sum_{i-1}))_{max}$
因为$sum_{n}$是常量,为了避免误会,令$sum_{n}=S$
日常考虑删$max$咯
$f_{i}=f_{j}+sum_{i-1}\cdot S-sum_{i}^{2}-sum_{k}\cdot S+sum_{j}\cdot sum_{i-1}$
$f_{j}=-sum_{i-1}\cdot sum_{j}+f_{i}-sum_{i-1}\cdot S+sum_{i}^{2}$
于是就设$f_{j}=y,-sum_{i-1}=k,sum_{j}=x,f_{i}-sum_{i-1}\cdot S+sum_{i}^{2}=b$
$max$于是考虑上凸壳昂,然后应该就差不多辣,,,?$QwQ$
没写完,,,存个半成品先$QAQ$#pragma GCC optimize(2) #include<bits/stdc++.h> using namespace std; #define il inline #define lf double #define gc getchar() #define int long long #define mp make_pair #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) const int N=1e6+10,inf=1e9,M=200+10; int n,stck[N],head,tail,f[N],g[N],sum[N],K,pre[N][M]; il int read() { ri x=0;rb y=1;rc ch=gc; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:-x; } il int sqr(ri x){return 1ll*x*x;} il lf X(ri x){return (lf)sum[x];} il lf Y(ri x){return (lf)g[x];} il lf slope(ri i,ri j){return (lf)(Y(i)-Y(j))/(X(i)-X(j));} signed main() { //freopen("3648.in","r",stdin);freopen("3648.out","w",stdout); n=read();K=read();rp(i,1,n)sum[i]=read()+sum[i-1]; rp(k,1,K) { head=tail=0; rp(i,k-1,n) { while(head<tail && slope(stck[head],stck[head+1])>-sum[i-1])++head; ri j=stck[head];f[i]=g[j]+(sum[i-1]-sum[j])*(sum[n]-sum[i-1]);pre[i][k]=j; while(head<tail && slope(stck[tail-1],i)>slope(stck[tail-1],stck[tail]))--tail;stck[++tail]=i; } rp(i,k-1,n)g[i]=f[i]; } ri tmp=n;rp(i,1,n)if(f[i]>f[tmp])tmp=i;printf("%lld\n",f[tmp]);my(i,K,2)printf("%lld ",tmp=pre[tmp][i]); return 0; } /*fi=(fk+(sumi−1−sumk)⋅(sumn−sumi−1))max*/ /*fj=y,−sumi−1=k,sumj=x,fi−sumi−1⋅S+sum2i=b*/