题目大意

将一个长度为N的非负整数序列分割成k+l个非空的子序列,每次选择一位置分割后,将会得到一定的分数,这个分数为两个新序列中元素和的乘积。求最大的分数。

[UOJ104]并输出任意一种方案

思路

显然,无论分割顺序如何,不会影响最后得到的结果。所以可以利用递推方程。\(f[i][j]\)表示取前\(i\)个数,分割成\(j\)个序列能得到的最大分数。显然有:

\[f[i][k]=max(f[j][k-1]+sum[j]*(sum[i]-sum[k]))
\]

当\(Ans_{j_1}>Ans_{j_2}\)时,有:

\[f[j_1][k-1]+sum[j_1]*sum[i]-sum[j_1]^2
\]

\[\gt
\]

\[f[j_2][k-1]+sum[j_2]*sum[i]-sum[j_2]^2
\]

令\(x[i]=f[i][k-1]-sum[i]^2,y[i]=sum[i]\)

则有:

\[-sum[i]<\frac{x[j_1]-x[j_2]}{y[j_1]-y[j_2]}
\]

注意点

计算斜率的时候\(x_1\)可能等于\(x_2\),特判一下将斜率设为INF或-INF。不要忘记开long long。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=100100;
const int MAXK=250;
int n,k;
LL sum[MAXN],x[2][MAXN],g[MAXN],y[MAXN],f[MAXN][2];
int cur; void init()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
y[i]=sum[i]=sum[i-1]+tmp;
g[i]=-y[i];
}
} LL dp()
{
memset(f,0,sizeof(f));
cur=0;
for (int i=1;i<=n;i++) x[1-cur][i]=f[i][cur]-(sum[i]*sum[i]);
for (int j=2;j<=k+1;j++)
{
cur=1-cur;
int head=0,tail=1,que[MAXN];
for (int i=j-1;i<=n;i++)//上一次至多分割为j-1部分,则至少从j-1开始
{
while (head+1<tail && x[cur][que[head]]-x[cur][que[head+1]]<=g[i]*(y[que[head]]-y[que[head+1]])) head++;
int best=que[head];
f[i][cur]=f[best][1-cur]+sum[best]*(sum[i]-sum[best]);
while (head+1<tail && (LL)(x[cur][que[tail-1]]-x[cur][i])*(y[que[tail-2]]-y[que[tail-1]])>=(LL)(x[cur][que[tail-2]]-x[cur][que[tail-1]])*(y[que[tail-1]]-y[i])) tail--;
que[tail++]=i;
x[1-cur][i]=f[i][cur]-(sum[i]*sum[i]);
}
}
return (f[n][cur]);
} void printans()
{
printf("%lld\n",dp());
} int main()
{
init();
printans();
return 0;
}

输出方案

只需记录一下路径就好了。不过要注意,UOJ后面数据时间卡得非常可啪,所以我们不用斜率而是直接用乘法来计算,同时x数组y数组g数组也不要了直接套进去算,勉勉强强卡过了……[痛心疾首.jpg]

顺带一提的是,这样的话BZOJ会T(咦?)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=100100;
const int MAXK=250;
int n,k;
LL sum[MAXN],f[MAXN][2],fr[MAXN][MAXK];
int cur; void init()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
sum[i]=sum[i-1]+tmp;
}
} LL x(int m)
{
return f[m][1-cur]-sum[m]*sum[m];
} LL dp()
{
memset(f,0,sizeof(f));
cur=0;
for (int j=2;j<=k+1;j++)
{
cur=1-cur;
int head=0,tail=1,que[MAXN];
for (int i=j-1;i<=n;i++)//上一次至多分割为j-1部分,则至少从j-1开始
{
while (head+1<tail && (f[que[head]][1-cur]-(LL)(sum[que[head]]*sum[que[head]])-f[que[head+1]][1-cur]+(LL)(sum[que[head+1]]*sum[que[head+1]])<=(LL)-sum[i]*(sum[que[head]]-sum[que[head+1]]))) head++;
int best=que[head];
f[i][cur]=f[best][1-cur]+sum[best]*(sum[i]-sum[best]);
fr[i][j]=best;
while (head+1<tail && (LL)(x(que[tail-1])-x(i))*(sum[que[tail-2]]-sum[que[tail-1]])>=(LL)(x(que[tail-2])-x(que[tail-1]))*(sum[que[tail-1]]-sum[i])) tail--;
que[tail++]=i;
}
}
return (f[n][cur]);
} void printans()
{
printf("%lld\n",dp());
int ans[MAXK];
memset(ans,0,sizeof(ans));//不要忘记初始化★
for (int i=k+1;i>=2;i--)
{
ans[++ans[0]]=fr[n][i];
n=fr[n][i];
}
for (int i=ans[0];i>=1;i--) printf("%d ",ans[i]);
} int main()
{
init();
printans();
return 0;
}

【斜率优化】bzoj3675-[Apio2014]序列分割&&Uoj104的更多相关文章

  1. bzoj3675[Apio2014]序列分割 斜率优化dp

    3675: [Apio2014]序列分割 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3508  Solved: 1402[Submit][Stat ...

  2. [Bzoj3675][Apio2014]序列分割(斜率优化)

    3675: [Apio2014]序列分割 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 4021  Solved: 1569[Submit][Stat ...

  3. BZOJ3675 [Apio2014]序列分割 【斜率优化dp】

    3675: [Apio2014]序列分割 Time Limit: 40 Sec  Memory Limit: 128 MB Submit: 3366  Solved: 1355 [Submit][St ...

  4. BZOJ3675 [Apio2014]序列分割 动态规划 斜率优化

    原文链接http://www.cnblogs.com/zhouzhendong/p/8697258.html 题目传送门 - BZOJ3675 题意 对于一个非负整数序列,小H需要重复k次以下的步骤: ...

  5. BZOJ3675: [Apio2014]序列分割(斜率优化)

    Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 4186  Solved: 1629[Submit][Status][Discuss] Descript ...

  6. 2018.09.29 bzoj3675: [Apio2014]序列分割(斜率优化dp)

    传送门 斜率优化dp经典题目. 首先需要证明只要选择的K个断点是相同的,那么得到的答案也是相同的. 根据分治的思想,我们只需要证明有两个断点时成立,就能推出K个断点时成立. 我们设两个断点分成的三段连 ...

  7. BZOJ3675 Apio2014 序列分割 【斜率优化】

    Description 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序列,小H需要重复k次以下的步骤: 1.小H首 ...

  8. [luogu3648][bzoj3675][APIO2014]序列分割【动态规划+斜率优化】

    题目大意 让你把一个数列分成k+1个部分,使分成乘积分成各个段乘积和最大. 分析 首先肯定是无法开下n \(\times\) n的数组,那么来一个小技巧:因为我们知道k的状态肯定是从k-1的状态转移过 ...

  9. bzoj3675: [Apio2014]序列分割

    留坑 为什么别人家的斜率优化跟我一点都不一样! 为什么斜率都要变成正的... 为什么要那么推式子 为什么不能直接做啊..... 为什么不把0去掉去秒WA啊 为什么叉积去了0也过不了啊 woc啊 #in ...

随机推荐

  1. 头像截取 图片上传 js插件

    先看一下整体效果 页面html <div class="row"> <div class="tabs-container"> <u ...

  2. splay:优雅的区间暴力!

    万年不更的blog主更新啦!主要是最近实在忙,好不容易才从划水做题的时间中抽出一段时间来写这篇blog 首先声明:这篇blog写的肯定会很基础...因为身为一个蒟蒻深知在茫茫大海中找到一个自己完全能够 ...

  3. Linux 入门记录:十七、Linux 命令行文本/文件处理工具

    一.文件浏览 cat 查看文件内容 more 以翻页形式查看文件内容(只能向下翻页) less 以翻页形式查看文件内容(可以上下翻页) head 查看文件的头几行(默认10行) tail 查看文件的尾 ...

  4. monkey测试===Monkey测试策略(系列二)转

    Monkey的测试策略 一. 分类 Monkey测试针对不同的对象和不同的目的采用不同的测试方案,首先测试的对象.目的及类型如下: 测试的类型分为:应用程序的稳定性测试和压力测试 测试对象分为:单一a ...

  5. centos7下opencv的安装

    os:centos7 opencv:opencv3.0.0 for linux reference:http://www.cnblogs.com/xixixing/p/6096057.html det ...

  6. HDU 6119 小小粉丝度度熊 双指针

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6119 题意:中文题面. 解法:先处理可能交叉的区间,然后容易发现满足双指针的特性. //HDU 611 ...

  7. Xcode7 iOS9.0 的真机调试

    Xcode7的真机调试: 1.Xcode偏好 -> 账号 -> 增加 Apple ID ->显示 free 2.Target 运行 iOS 版本号 3.修正 Team 项   选择 ...

  8. IDEA 内存设置

    -server -Xms2g -Xmx2g -XX:NewRatio=3 -Xss16m -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled - ...

  9. 从设计图到CSS:rem+viewport+媒体查询+Sass

    根据UI图对移动端的h5页面做样式重构,是前端工程师的本职工作,看似简单,不过想做好却并不容易.下面总结一下其中要点. rem rem是一种相对长度单位,参考的基准是<html>标签定义的 ...

  10. Jquery动态添加元素并给元素增加onchange相应

    动态添加元素: $select = $("<select></select>"); $("<option></option> ...