LG3648 [APIO2014]序列分割
题意
你正在玩一个关于长度为 \(n\) 的非负整数序列的游戏。这个游戏中你需要把序列分成 \(k+1\) 个非空的块。为了得到 \(k+1\) 块,你需要重复下面的操作 \(k\) 次:
选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。
\(2≤n≤100000,1≤k≤\min\{n−1,200\}\)
分析
参照Tunix的题解。
首先我们根据这个分割的过程可以发现:总得分等于k+1段两两的乘积的和(乘法分配律),也就是说与分割顺序是无关的。
再对乘积进行重分组(还是乘法分配律)我们可以转化为:ans=∑第 i 段×前 i-1 段的和
所以我们就可以以分割次数为阶段进行DP啦~
令\(f[i][j]\)表示将前 \(j\) 个数分成 \(i\) 段的最大得分,那么就有
\[
f[i][j]=\max\{f[i−1][k]+s[k]×(s[j]−s[k])\}
\]
这里我前些时候一直规范要求的作用就体现出来了。
令\(x>y\),且\(x\)比\(y\)优,则
\[
f[i-1][x]-s[x]^2+s[x]s[j]>f[i-1][y]-s[y]^2+s[y]s[j]
\]
这里就不能乱移项,必须保证除式中的\(\varphi(x)-\varphi(y)\)值是正的才谈得上平面中的点。必须把\(s[j]\)的系数调整成\(s[x]-s[y]\)。
\[
\frac{s[x]^2-f[i-1][x]-s[y]^2+f[i-1][y]}{s[x]-s[y]}<s[j]
\]
这才是正确的斜率式,跟前面的\(f[i-1][x]-s[x]^2\)是反的。
还是多次的斜率优化,洛谷上还要输出方案数,这也比较简单,每次转移的时候记一个\(g\)数组记录就行了。
时间复杂度\(O(kn)\)。
代码
首先是BZOJ同名题,卡空间需要滚动数组的。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>T read(T&x)
{
return x=read<T>();
}
using namespace std;
typedef long long ll;
co int K=201,N=1e5+1;
ll s[N];
ll f[2][N]; // edit 1: MLE
ll Up(int i,int x,int y)
{
return s[x]*s[x]-f[i^1][x]-s[y]*s[y]+f[i^1][y];
}
ll Down(int x,int y)
{
return s[x]-s[y];
}
ll Cal(int i,int j,int k)
{
return f[i^1][k]+s[k]*(s[j]-s[k]);
}
int q[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// cerr<<"sizef="<<(sizeof(f)+sizeof(s)+sizeof(q))/1024.0/1024<<endl;
int n=read<int>(),k=read<int>();
for(int i=1;i<=n;++i)
s[i]=s[i-1]+read<int>();
for(int i=1;i<=k;++i)
{
int head=0,tail=0;
q[tail++]=0;
for(int j=1;j<=n;++j)
{
while(head+1<tail&&Up(i&1,q[head+1],q[head])<=s[j]*Down(q[head+1],q[head]))
++head;
f[i&1][j]=Cal(i&1,j,q[head]);
while(head+1<tail&&Up(i&1,j,q[tail-1])*Down(q[tail-1],q[tail-2])<=Up(i&1,q[tail-1],q[tail-2])*Down(j,q[tail-1]))
--tail;
q[tail++]=j;
}
}
printf("%lld\n",f[k&1][n]);
return 0;
}
然后是洛谷上需要输出方案,但不卡空间的。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>T read(T&x)
{
return x=read<T>();
}
using namespace std;
typedef long long ll;
co int K=201,N=1e5+1;
ll s[N];
ll f[K][N];
int g[K][N];
ll Up(int i,int x,int y)
{
return s[x]*s[x]-f[i-1][x]-s[y]*s[y]+f[i-1][y];
}
ll Down(int x,int y)
{
return s[x]-s[y];
}
ll Cal(int i,int j,int k)
{
return f[i-1][k]+s[k]*(s[j]-s[k]);
}
int q[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// cerr<<"sizef="<<(sizeof(f)+sizeof(s)+sizeof(q))/1024.0/1024<<endl;
int n=read<int>(),k=read<int>();
for(int i=1;i<=n;++i)
s[i]=s[i-1]+read<int>();
for(int i=1;i<=k;++i)
{
int head=0,tail=0;
q[tail++]=0;
for(int j=1;j<=n;++j)
{
while(head+1<tail&&Up(i,q[head+1],q[head])<=s[j]*Down(q[head+1],q[head]))
++head;
f[i][j]=Cal(i,j,q[head]);
g[i][j]=q[head];
while(head+1<tail&&Up(i,j,q[tail-1])*Down(q[tail-1],q[tail-2])<=Up(i,q[tail-1],q[tail-2])*Down(j,q[tail-1]))
--tail;
q[tail++]=j;
}
}
printf("%lld\n",f[k][n]);
stack<int>sol;
for(int i=k,j=n;i>=1;--i)
{
j=g[i][j];
sol.push(j);
}
while(sol.size())
{
printf("%d ",sol.top());
sol.pop();
}
return 0;
}
个人比较喜欢洛谷上的题。卡空间算一种恶心的卡常,况且写起来还那么丑。
LG3648 [APIO2014]序列分割的更多相关文章
- 【斜率DP】BZOJ 3675:[Apio2014]序列分割
3675: [Apio2014]序列分割 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1066 Solved: 427[Submit][Statu ...
- BZOJ 3675: [Apio2014]序列分割( dp + 斜率优化 )
WA了一版... 切点确定的话, 顺序是不会影响结果的..所以可以dp dp(i, k) = max(dp(j, k-1) + (sumn - sumi) * (sumi - sumj)) 然后斜率优 ...
- bzoj3675[Apio2014]序列分割 斜率优化dp
3675: [Apio2014]序列分割 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3508 Solved: 1402[Submit][Stat ...
- BZOJ_3675_[Apio2014]序列分割_斜率优化
BZOJ_3675_[Apio2014]序列分割_斜率优化 Description 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了 ...
- 斜率优化入门学习+总结 Apio2011特别行动队&Apio2014序列分割&HZOI2008玩具装箱&ZJOI2007仓库建设&小P的牧场&防御准备&Sdoi2016征途
斜率优化: 额...这是篇7个题的题解... 首先说说斜率优化是个啥,额... f[i]=min(f[j]+xxxx(i,j)) ; 1<=j<i (O(n^2)暴力)这样一个式子,首 ...
- P3648 [APIO2014]序列分割(斜率优化dp)
P3648 [APIO2014]序列分割 我们先证明,分块的顺序对结果没有影响. 我们有一个长度为3的序列$abc$ 现在我们将$a,b,c$分开来 随意枚举一种分块方法,如$(ab)(c)$,$(a ...
- [luogu P3648] [APIO2014]序列分割
[luogu P3648] [APIO2014]序列分割 题目描述 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序 ...
- 洛谷 P3648 [APIO2014]序列分割 解题报告
P3648 [APIO2014]序列分割 题目描述 你正在玩一个关于长度为\(n\)的非负整数序列的游戏.这个游戏中你需要把序列分成\(k+1\)个非空的块.为了得到\(k+1\)块,你需要重复下面的 ...
- [APIO2014]序列分割 --- 斜率优化DP
[APIO2014]序列分割 题目大意: 你正在玩一个关于长度为\(n\)的非负整数序列的游戏.这个游戏中你需要把序列分成\(k+1\)个非空的块.为了得到\(k+1\)块,你需要重复下面的操作\(k ...
随机推荐
- MATLAB安装libsvm工具箱的方法
支持向量机(support vector machine,SVM)是机器学习中一种流行的学习算法,在分类与回归分析中发挥着重要作用.基于SVM算法开发的工具箱有很多种,下面我们要安装的是十分受欢迎的l ...
- 华为S5700系列交换机配置通过流策略实现VLAN间三层隔离
组网图形 图1 配置通过流策略实现VLAN间三层隔离组网图 组网需求 如图一所示,为了通信的安全性,某公司将访客.员工.服务器分别划分到VLAN10.VLAN20.VLAN30中.公司希望: 员工.服 ...
- HDU 1238 Substing
思路: 1.找出n个字符串中最短的字符串Str[N] 2.从长到短找Str[N]的子子串 subStr[N],以及subStr[N]的反转字符串strrev(subStr[N]):(从长到短是做剪枝处 ...
- spring集成spring mvc 和hibernate详解
1.配置IOC容器 <!-- 配置IOC容器 --> <context-param> <param-name>contextConfigLocation</p ...
- SpringMVC的HelloWorld快速入门!
1.加入JAR包: commons-logging-1.1.3.jar spring-aop-4.0.0.RELEASE.jar spring-beans-4.0.0.RELEASE.jar spri ...
- 删除读取失败的服务 <读取描述失败错误代码2>
1.以管理员身份运行命令提示符 2.sc delete 服务名
- java--何时处理Exception(哪一个层级),包装的基础类处理任务尽可能简洁,写入日志,检查null等运行时异常
1. 运行时异常和受检异常 2. 提前预防运行时异常.最常发生的是NPE,而检查NPE是程序员的基本职责.其他的,如除0等运行时异常的检查,需要程序员仔细检查,每个函数都得检查(除非可以确定不会有空指 ...
- php源码之appveyor
打开appveyor目录 是4个bat文件 1. .bat是什么? bat文件是dos下的批处理文件.批处理文件是无格式的文本文件,它包含一条或多条命令. 它的文件扩展名为.bat或.cmd 在命令提 ...
- java sleep()和wait()的区别
java sleep()和wait()的区别? sleep()和wait()都能阻塞当前线程. 区别1: sleep()属于Thread类:wait()属于Object类. 区别2: 调用sleep( ...
- org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 语法分析器在此文档中遇到多个 "64,000" 实体扩展; 这是应用程序施加的限制
使用SAX解析XML文件.XML文件有1.5G,程序抛出了这个问题: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 语法 ...