【洛谷3648】[APIO2014] 序列分割(斜率优化DP)
大致题意: 你可以对一个序列进行\(k\)次分割,每次得分为两个块元素和的乘积,求总得分的最大值。
区间\(DPor\)斜率优化\(DP\)
这题目第一眼看上去感觉很明显是区间\(DP\)。
但是,一看数据范围,\(n\le100000\),这是要上天的节奏!
不过,再看\(m\le200\),比较显然应该是\(O(nm)\)的时间复杂度。
实际上,这题的确是可以用斜率优化\(DP\)来做到\(O(nm)\)的。
推性质
首先,我们要知道一个性质:将一个区间进行若干次分割,分割的顺序是不影响最后的总得分的。
证明如下:
设要对一个区间进行\(2\)次分割,则分割完后有\(3\)个区间,每个区间的价值和分别为\(a_1,a_2,a_3\)。
则总共有两种分割顺序,得到的总得分分别为\(a_1·(a_2+a_3)+a_2·a_3\)和\((a_1+a_2)·a_3+a_1·a_2\)。
实际上,这两个式子化简后皆为\(a_1a_2+a_1a_3+a_2a_3\)。
而多次分割其实是同理的。
状态转移
这样一来,就不难想到针对这种问题的常见套路:设\(f_{i,j}\)表示在前\(i\)个数中割\(j\)次得到的最大总得分。
状态转移方程如下:
\]
这应该是比较好理解的吧,就相当于枚举一个割的位置,总得分为该区间前半部分割\(j-1\)次的最大得分加上这两部分元素和的乘积。
对于这种式子,比较显然可以斜率优化。
将原式展开得:
\]
要注意在转移过程中可能会出现分母为\(0\)的情况,则需要给此时的斜率赋值为\(-INF\)。
由于要记方案,所以再用一个\(g\)数组记录下从哪个元素转移而来即可。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define abs(x) ((x)<0?-(x):(x))
#define swap(x,y) (x^=y^=x^=y)
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define INF 1e18
#define N 100000
#define M 200
using namespace std;
int n,m,sum[N+5];
class Class_FIO
{
private:
#define Fsize 100000
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
#define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
int f,FoutSize,Top;char ch,Fin[Fsize],*A,*B,Fout[Fsize],Stack[Fsize];
public:
Class_FIO() {A=B=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_digit(int &x) {while(!isdigit(x=tc()));x&=15;}
inline void readc(char &x) {while(isspace(x=tc()));}
inline void reads(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())&&~ch);}
inline void write(LL x) {if(!x) return pc('0');x<0&&(pc('-'),x=-x);while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
inline void writec(char x) {pc(x);}
inline void writes(string x) {for(register int i=0,len=x.length();i<len;++i) pc(x[i]);}
inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
}F;
class Class_SlopeDP
{
private:
#define A(x) (f[x][j-1]-1LL*sum[x]*sum[x])
#define B(x) (-1LL*sum[x])
#define S(x,y) (B(x)^B(y)?1.0*(A(y)-A(x))/(B(y)-B(x)):-INF)//对于分母为0的情况,返回-INF
#define Slope (1LL*sum[i])
int q[N+5],g[N+5][M+5];LL f[N+5][M+5];
inline void PrintStep(int x,int y) {y&&(PrintStep(g[x][y-1],y-1),F.write(x),F.writec(' '),0);}//输出方案
public:
inline void Solve()//DP转移
{
register int i,j,H,T;
for(j=1;j<=m;++j)
{
for(q[H=T=1]=0,i=1;i<=n;++i)//每次记得清空队列
{
while(H<T&&S(q[H],q[H+1])<=Slope) ++H;
f[i][j]=f[g[i][j]=q[H]][j-1]+1LL*sum[q[H]]*(sum[i]-sum[q[H]]);
while(H<T&&S(q[T],i)<=S(q[T-1],q[T])) --T;
q[++T]=i;
}
}
}
inline void Print() {F.write(f[n][m]),F.writec('\n'),PrintStep(g[n][m],m);}
}SlopeDP;
int main()
{
register int i;
for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(sum[i]),sum[i]+=sum[i-1];
return SlopeDP.Solve(),SlopeDP.Print(),F.clear(),0;
}
【洛谷3648】[APIO2014] 序列分割(斜率优化DP)的更多相关文章
- bzoj3675[Apio2014]序列分割 斜率优化dp
3675: [Apio2014]序列分割 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3508 Solved: 1402[Submit][Stat ...
- [APIO2014]序列分割 --- 斜率优化DP
[APIO2014]序列分割 题目大意: 你正在玩一个关于长度为\(n\)的非负整数序列的游戏.这个游戏中你需要把序列分成\(k+1\)个非空的块.为了得到\(k+1\)块,你需要重复下面的操作\(k ...
- 【bzoj3675】[Apio2014]序列分割 斜率优化dp
原文地址:http://www.cnblogs.com/GXZlegend/p/6835179.html 题目描述 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列 ...
- 洛谷3648 [APIO2014]序列分割(斜率优化+dp)
首先对于这个题目. qwq 存在一个性质就是,最终的答案只跟你的分割的位置有关,而和顺序无关. 举一个小栗子 \(a\ b\ c\) 将这个东西分成两块. 如果我们先分割\(ab\)之间的话,\(an ...
- BZOJ 3675 [Apio2014]序列分割 (斜率优化DP)
洛谷传送门 题目大意:让你把序列切割k次,每次切割你能获得 这一整块两侧数字和的乘积 的分数,求最大的分数并输出切割方案 神题= = 搞了半天也没有想到切割顺序竟然和答案无关...我太弱了 证明很简单 ...
- 洛谷 P3648 [APIO2014]序列分割 解题报告
P3648 [APIO2014]序列分割 题目描述 你正在玩一个关于长度为\(n\)的非负整数序列的游戏.这个游戏中你需要把序列分成\(k+1\)个非空的块.为了得到\(k+1\)块,你需要重复下面的 ...
- P3648 [APIO2014]序列分割 斜率优化
题解:斜率优化\(DP\) 提交:\(2\)次(特意没开\(long\ long\),然后就死了) 题解: 好的先把自己的式子推了出来: 朴素: 定义\(f[i][j]\)表示前\(i\)个数进行\( ...
- 洛谷P2365 任务安排(斜率优化dp)
传送门 思路: 最朴素的dp式子很好考虑:设\(dp(i,j)\)表示前\(i\)个任务,共\(j\)批的最小代价. 那么转移方程就有: \[ dp(i,j)=min\{dp(k,j-1)+(sumT ...
- 洛谷P2120 [ZJOI2007]仓库建设 斜率优化DP
做的第一道斜率优化\(DP\)QwQ 原题链接1/原题链接2 首先考虑\(O(n^2)\)的做法:设\(f[i]\)表示在\(i\)处建仓库的最小费用,则有转移方程: \(f[i]=min\{f[j] ...
- BZOJ 3675 APIO2014 序列切割 斜率优化DP
题意:链接 方法:斜率优化DP 解析:这题BZ的数据我也是跪了,特意去网上找到当年的数据后面二十个最大的点都过了.就是过不了BZ. 看到这道题自己第一发DP是这么推得: 设f[i][j]是第j次分第i ...
随机推荐
- IT兄弟连 JavaWeb教程 jQuery对AJAX的支持经典面试题
1.简述对Ajax的理解 AJAX是Asynchronous JavaScript and Xml异步的JavaScript和Xml.它一种用来改善用户体验的技术其实质是使用XMLHttpReques ...
- Java学习笔记——Map接口
Map接口 Map接口 Map接口中键和值一一映射. 可以通过键来获取值. 异常 NoSuchElementException:访问的值不存在 ClassCastException:对象类型错误 Un ...
- Ubuntu16.04双网卡绑定
服务器经常有多个网卡,为了保证网络冗余性,一个网卡出现故障时,不导致网络服务中断,可以懂多网卡网卡绑定来解决此问题. 环境: 系统:Ubuntu16.04 网卡:em1 em2 ip:192.168. ...
- Luogu P4144 大河的序列 贪心+脑子
首先向颜神犇致敬...还是自己太菜,又不仔细思考,上来就翻题解$qwq$ 首先有一种贪心方法:即,$ans=2*max(dirty_i)$ 证明:若现在的答案为$ans$,考虑一个新的数$x$对答案的 ...
- word2010页脚页码的总页数修改方法
3很多时候做WORD文档时,首页和尾页通常是做为封面与封底的是不做页码统计的. 这时候就需要总页面上减去首页和尾页的数量.以下为修改总页数方法 1.打开WORD文档设置页眉页脚,页脚设置页码, 2.设 ...
- python排序(冒泡、直接选择、直接插入等)
冒泡排序 冒泡法:第一趟:相邻的两数相比,大的往下沉.最后一个元素是最大的. 第二趟:相邻的两数相比,大的往下沉.最后一个元素不用比. #冒泡排序 array = [1,5,6,2,9,4,3] de ...
- 03-----body标签中的相关标签
今日主要内容: 列表标签 <ul>.<ol>.<dl> 表格标签 <table> 表单标签 <form> 一.列表标签 1.无序列表< ...
- python操作json文件
import json class OperationJson(object): def __init__(self,file_name=None): if file_name: self.file_ ...
- Java面向对象_简单工厂模式
概念:由一个工厂对象决定创建出哪一种产品类的实例. public class Practice14 { public static void main(String[] args) { // TODO ...
- js固定两位小数toFixed(2)
total=total.toFixed(3); 小数问题:可以number(),或者*1来改变变量类型.