@desription@

给定一个序列 a,定义它的权值 \(c = \sum_{i=1}^{n}a_i\)。

你可以做如下的操作恰好一次:选择一个数,然后将它移动到一个位置(可以是原位置,序列开头与结尾)。

最大化序列权值。

input

第 1 行一个整数 n,表示序列长度(2 <= n <= 200000)。

第 2行 n 个整数 a1, a2, ..., an,表示这个序列(|ai| <= 1000000)。

output

输出一个整数,表示最大的序列权值。

sample input

4

4 3 2 5

sample output

39

sample explain

将 4 移动到 5 之前,得到 \(c = 1*3 + 2*2 + 3*4 + 4*5 = 39\)。

@solution@

移动可以向前移动也可以向后移动,我们仅考虑向后这一种,向前同理。

记原序列权值为 \(c\),再记 \(s[i]=\sum_{p=1}^{i}a_p\)。

考虑将第 i 号元素移动到第 j 个位置,则新序列权值为:

\[c'=c-a[i]*i+a[i]*j+(s[j]-s[i])
\]

你看,它多么的斜率优化。

求最大值是上凸包,横坐标为 \(-j\),从后往前是单增的。

但是……斜率为 \(a[i]\),是不单调的。

所以我们必须在凸包上作二分寻找答案。

一开始我很懵逼,凸包不应该是三分求极值吗?后来我才发现,二分原来是二分斜率。凸包上斜率是单增的,所以可以使用二分。(但是三分好像也可以……只是大概没人想写而已……明明三分更容易调错来着 qwq)。

二分找什么呢?就是找到一个点,它和它前驱的斜率大于等于 \(a[i]\),它和它后继的斜率小于等于 \(a[i]\)。

注意二分常见的错误:边界。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 200000;
int n;
ll a[MAXN + 5], s1[MAXN + 5], s2[MAXN + 5];
ll c1(int i) {return s1[n] - a[i]*i + s2[i-1];}
ll c2(int i) {return s1[n] - a[i]*i + s2[i];}
ll k1(int i) {return -a[i];}
ll k2(int i) {return a[i];}
ll x1(int j) {return j;}
ll x2(int j) {return -j;}
ll y1(int j) {return -s2[j-1];}
ll y2(int j) {return -s2[j];}
int stk[MAXN + 5], tp;
double slope1(int p, int q) {return 1.0*(y1(p) - y1(q))/(x1(p) - x1(q));}
double slope2(int p, int q) {return 1.0*(y2(p) - y2(q))/(x2(p) - x2(q));}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%lld", &a[i]);
for(int i=1;i<=n;i++) {
s1[i] = s1[i-1] + a[i]*i;
s2[i] = s2[i-1] + a[i];
}
ll ans = -(1LL<<62); tp = 0;
for(int i=1;i<=n;i++) {
while( tp > 1 && slope1(stk[tp - 1], stk[tp]) <= slope1(stk[tp], i) )
tp--;
stk[++tp] = i;
int le = 1, ri = tp;
while( le < ri ) {
int mid = (le + ri) >> 1;
if( slope1(stk[mid], stk[mid+1]) <= k1(i) ) ri = mid;
else le = mid + 1;
}
ans = max(ans, c1(i) + y1(stk[le]) - k1(i)*x1(stk[le]));
}
tp = 0;
for(int i=n;i>=1;i--) {
while( tp > 1 && slope2(stk[tp - 1], stk[tp]) <= slope2(stk[tp], i) )
tp--;
stk[++tp] = i;
int le = 1, ri = tp;
while( le < ri ) {
int mid = (le + ri) >> 1;
if( slope2(stk[mid], stk[mid+1]) <= k2(i) ) ri = mid;
else le = mid + 1;
}
ans = max(ans, c2(i) + y2(stk[le]) - k2(i)*x2(stk[le]));
}
printf("%lld\n", ans);
}

@details@

一开始我从前往后和从后往前都用同一个横坐标,然后因为枚举顺序不一样,导致一个是单增的一个是单减的。

单增的还好,单减的那个让我二分时各种边界错误……调到死都调不出来……

最后索性把单减那个横坐标取个相反数,变成单增的。然后一遍过 =_=。

@codeforces - 631E@ Product Sum的更多相关文章

  1. Codeforces 631E Product Sum 斜率优化

    我们先把问题分成两部分, 一部分是把元素往前移, 另一部分是把元素往后移.对于一个 i 后的一个位置, 我们考虑前面哪个移到这里来最优. 我们设最优值为val,   val = max(a[ j ] ...

  2. Codeforces Round #344 (Div. 2) E. Product Sum 维护凸壳

    E. Product Sum 题目连接: http://www.codeforces.com/contest/631/problem/E Description Blake is the boss o ...

  3. Codeforces Round #344 (Div. 2) E. Product Sum 二分斜率优化DP

    E. Product Sum   Blake is the boss of Kris, however, this doesn't spoil their friendship. They often ...

  4. Codeforces 396B On Sum of Fractions 数论

    题目链接:Codeforces 396B On Sum of Fractions 题解来自:http://blog.csdn.net/keshuai19940722/article/details/2 ...

  5. [codeforces631E]Product Sum

    E. Product Sum time limit per test: 1 second memory limit per test: 256 megabytes input:standard inp ...

  6. codeforces 963A Alternating Sum

    codeforces 963A Alternating Sum 题解 计算前 \(k\) 项的和,每 \(k\) 项的和是一个长度为 \((n+1)/k\) ,公比为 \((a^{-1}b)^k\) ...

  7. codeforces 1217E E. Sum Queries? (线段树

    codeforces 1217E E. Sum Queries? (线段树 传送门:https://codeforces.com/contest/1217/problem/E 题意: n个数,m次询问 ...

  8. Codeforces 577B Modulo Sum

    http://codeforces.com/problemset/problem/577/B 题意:有n个数,求有无一个子序列满足和是m的倍数 思路:用模下的背包做,发现n是十的六次方级别,但是有个神 ...

  9. Codeforces 85 D. Sum of Medians

    题目链接:http://codeforces.com/contest/85/problem/D 做法果然男默女泪啊..... 大概就是直接开了一个$vector$每次插入删除都用自带的$insert$ ...

随机推荐

  1. OpenLayers测量距离和面积

    <!DOCTYPE html> <html> <head> <title>测量距离和面积</title> <meta http-equ ...

  2. htmlunit第一个爬虫演示 目标网址http://ent.sina.com.cn/film/

    基本都要放弃了 springmvc 配置了htmlunit之后无法运行,都不能正常实例化webclient,但是突然想起来用maven应用程序测试一下 结果竟然就可以了.好吧,还是有希望的 大佬博客 ...

  3. IntelliJ IDEA 中如何查看一个类的所有继承关系(当前类的所有继承关系图)

    IntelliJ IDEA 中如何查看一个类的所有继承关系(当前类的所有继承关系图) .embody{ padding:10px 10px 10px; margin:0 -20px; border-b ...

  4. NodeJS概述2-事件插件-简易爬虫

    事件 events 模块 原生事件写法 /* * 1. 事件分类 * DOM0级 事件 - on + eventType * DOM2级 事件 - 事件监听 * 2. 事件构成部分有哪些? dom.o ...

  5. oracle触发器级联删除

    create or replace trigger delete_trigger1 after delete on contracting_party for each row begin if de ...

  6. js 详解setTimeout定时器

    setTimeout: 定时器函数 第一个参数是匿名函数,第二个参数是延迟执行时间 setTimeout(function(){},time) 注意: 1.setTimeout函数是Window对象提 ...

  7. ASP.NET Core 中使用TypeScript

    ASP.NET Core 注意:Visual Studio 2017和最新版本的ASP.NET的更新即将推出! 安装 ASP.NET Core 和 TypeScript 首先,若有必要请安装 ASP. ...

  8. 用SpannableString来设置一个textview的各种样式

    通常情况下,textview 设置文本的样式很单一,为了满足某种需求可以使用SpannableString来设置文本字体大小.颜色.超链接等属性. xml就一个TextView所以就不在给出了,直接看 ...

  9. MAC+VS Code+Python+Markdown调试配置

    目录 VS Code官网下载 VS Code插件推荐 VS Code Python环境配置 Markdown配置 VS Code官方文档 VS Code官网下载 VS Code官网下载地址 VS Co ...

  10. Spring中使用DataSourceTransactionManager进行事务管理的xml配置

    在一个业务的实现过程中,可能需要多条sql完成对数据库的操作,比如账户登录,需要匹配用户名和密码,然后要增加积分,还要记录登录的ip和时间,这可能需要三个sql语句,这三个语句应当是一个整体,任意一个 ...