@bzoj - 2388@ 旅行规划
@description@
请你维护一个序列,支持两种操作:
(1)某个区间 [x, y] 内的数同时加上一个增量 k。
(2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和。
input
第一行给出一个整数 n。n <= 100000。接下来一行 n 个整数表示序列的初始值。
第三行给出一个整数 m,m <= 100000。接下来 m 行每行一个操作。
(1) 0 x y k:表示给区间 [x, y] 同时加上 k。
(2) 1 x y:询问区间 [x, y]。
output
对于每个询问,输出一个整数表示最大前缀和。
sample input
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
sample output
9
22
@solution@
我们考虑直接维护前缀和序列,则操作(2)就是在查询区间最大值。
而对于操作(1),我们相当于两部分操作:
对于 x <= i <= y,给 i 位置加上 (i-x+1)*k;对于 y < i,给 i 位置加上 (y-x+1)*k。
前一个可以拆成 (-x+1)*k + i*k,是常数 + 系数*位置的形式;后面那个也可以看成这种形式,只是位置前面的系数为 0。
看起来还是不好维护,但是我们可以注意到这样一件事情:对于某一个位置 i,它的值总是形如 k*i + b 的形式。
直线解析式。所以我们考虑用几何方法来维护这种东西。几何方法当然首先想到凸包。
每次操作相当于给区间内所有点的斜率与截距同时加上增量,手算一下会发现这个区间相邻两点之间的斜率也会同时增加 k,这样也就是说这个区间的凸包形状不会变化。
线段树不太好搞(况且这道题时限 50s ),我们考虑使用分块算法来维护凸包。
修改时,散块暴力修改并重构凸包,整块打标记,记录这个区间整体的斜率与截距变化量。
查询时,散块暴力求答案,整块凸包上二分。
时间复杂度 \(O(n\sqrt{n}\log n)\)
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
const int BLOCK = 320;
const ll INF = (1ll<<62);
ll sp[BLOCK + 5], b1[MAXN + 5], b2[BLOCK + 5];
int le[BLOCK + 5], ri[BLOCK + 5], num[MAXN + 5];
int stk[MAXN + 5], tp[BLOCK + 5], n, m, bcnt = 0;
ll get_ans(int x) {
return sp[num[x]]*x + b1[x] + b2[num[x]];
}
ll query(int x) {
int l = le[x], r = tp[x];
while( l < r ) {
int mid = (l + r) >> 1;
if( get_ans(stk[mid]) >= get_ans(stk[mid+1]) ) r = mid;
else l = mid + 1;
}
return get_ans(stk[r]);
}
void push_tag(int x) {
for(int i=le[x];i<=ri[x];i++)
b1[i] = get_ans(i);
sp[x] = b2[x] = 0;
}
void build(int x) {
tp[x] = le[x] - 1;
for(int i=le[x];i<=ri[x];i++) {
while( tp[x] > le[x] && (get_ans(i) - get_ans(stk[tp[x]]))*(stk[tp[x]] - stk[tp[x] - 1]) >= (get_ans(stk[tp[x]])-get_ans(stk[tp[x] - 1]))*(i - stk[tp[x]]) )
tp[x]--;
stk[++tp[x]] = i;
}
}
void init() {
for(int i=0;i<n;i++) {
if( i % BLOCK == 0 ) {
num[i] = (++bcnt);
le[num[i]] = ri[num[i]] = i;
sp[num[i]] = b2[num[i]] = 0;
}
else ri[num[i] = bcnt]++;
}
}
int main() {
scanf("%d", &n); init();
for(int i=0;i<n;i++)
scanf("%lld", &b1[i]), b1[i] += b1[i-1];
for(int i=1;i<=bcnt;i++)
build(i);
scanf("%d", &m);
for(int i=1;i<=m;i++) {
int op, x, y; ll k;
scanf("%d%d%d", &op, &x, &y);
x--, y--;
if( op == 0 ) {
scanf("%lld", &k);
if( num[x] != num[y] ) {
push_tag(num[x]), push_tag(num[y]);
for(int i=x;i<=ri[num[x]];i++)
b1[i] += k*(i-x+1);
for(int i=le[num[y]];i<=y;i++)
b1[i] += k*(i-x+1);
for(int i=y+1;i<=ri[num[y]];i++)
b1[i] += k*(y-x+1);
build(num[x]), build(num[y]);
for(int i=num[x]+1;i<=num[y]-1;i++)
sp[i] += k, b2[i] += k*(-x+1);
}
else {
push_tag(num[x]);
for(int i=x;i<=y;i++)
b1[i] += k*(i-x+1);
for(int i=y+1;i<=ri[num[y]];i++)
b1[i] += k*(y-x+1);
build(num[x]);
}
for(int i=num[y]+1;i<=bcnt;i++)
b2[i] += k*(y-x+1);
}
else {
ll ans = -INF;
if( num[x] != num[y] ) {
for(int i=x;i<=ri[num[x]];i++)
ans = max(ans, get_ans(i));
for(int i=le[num[y]];i<=y;i++)
ans = max(ans, get_ans(i));
for(int i=num[x]+1;i<=num[y]-1;i++)
ans = max(ans, query(i));
}
else {
for(int i=x;i<=y;i++)
ans = max(ans, get_ans(i));
}
printf("%lld\n", ans);
}
}
}
@details@
突然发现这是我第一次写分块?原来我以前从来没写过这种东西?
把分块的左端点右端点以及每个点属于哪个块先预处理出来感觉比较好写。
并且把块的大小设置为常数也是一个不错的懒人做法(虽然想想都知道这样肯定常数大)。
@bzoj - 2388@ 旅行规划的更多相关文章
- BZOJ 2388: 旅行规划 [分块 凸包 等差数列]
传送门 题意: 区间加和询问一段区间内整体前缀和的最大值 刚才还在想做完这道题做一道区间加等差数列结果发现这道就是.... 唯一的不同在于前缀和一段区间加上等差数列后,区间后面也要加上一个常数!!! ...
- BZOJ 2388--旅行规划(分块&单调栈&二分)
2388: 旅行规划 Time Limit: 50 Sec Memory Limit: 128 MBSubmit: 405 Solved: 118[Submit][Status][Discuss] ...
- bzoj 4501: 旅行 01分数规划+概率期望dp
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4501 题解: 首先我们不考虑可以删除边的情况下,如何计算期望边数. 然后我们发现这是个有 ...
- bzoj 3531 旅行
动态开点线段树+树链剖分 对于每一种宗教信仰都开一颗线段树 空间: QlogN 即每一次修改都只会改变logN 个点 时间 O(QlogN) naive题 边没有开两倍 QAQ bzoj 35 ...
- bzoj 4501 旅行
01分数规划+最大权闭合子图 倒拓扑序处理每个节点 $$f[x]=\frac{\sum{f[v]}}{n}+1$$ 二分答案$val$ 只需要判断是否存在$\sum{f[v]}+1-val>0$ ...
- 旅行规划(travel)
题目描述 OIVillage 是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl 决定修建了一条铁路将当地 nnn 个最著名的经典连接起来,让游客可以通过火车从 ...
- BZOJ2388:旅行规划(travel)——分块凸包
题目 OIVillage 是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl 决定修建了一条铁路将当地 $n$ 个最著名的经典连接起来,让游客可以通过火车从铁路 ...
- BZOJ 4464 旅行时的困惑 最小流
题面: Waldives 有 N 个小岛.目前的交通系统中包含 N-1 条快艇专线,每条快艇 专线连接两个岛.这 N-1条快艇专线恰好形成了一棵树. 由于特殊的原因,所有N-1条快艇专线都是单向的.这 ...
- 「BZOJ2388」旅行规划
传送门 分块+凸包 求出前缀和数组s 对于l~r加上k,相当于s[l]~s[r]加上一个首项为k,公差为k的等差数列.r~n加上k*(r-l+1). 分块之后对每一块维护两个标记,一个记录它加的等差数 ...
随机推荐
- Mac+Webstorm 双更新后 webstorm无法使用内置svn
我终于营业了!!!!!! EachTime!!!! 我更新了mac系统后,就会莫名其妙的webstorm的svn无法使用 具体表现为无法更新和提交 具体报错为:Can't use Subversion ...
- Leetcode17.Letter Combinations of a Phone Number电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23" 输出:[&quo ...
- web前端学习(二)html学习笔记部分(11)-- 没有标号记录的知识合集
这一部分内容相对比较简单,就不按规矩排序了.(主要是网站上也没有这一部分内容的排序) 1. html5的 非主体结构元素 学习笔记(1)里面记录过. 2. html5表单提交和PHP环境搭建 1. ...
- dijkstra算法 模板
算法理解见: https://www.bilibili.com/video/av18586085/?p=83 模板: #define INF 1000000000 int N; int dist[10 ...
- js this工作原理
js中的this是个很妙的东西,你经常不知道它到底在指向谁,又是谁在调用它. 通用判断方法: 1.this总是指向它的直接调用者 var a={ user:'Artimis', fn:function ...
- DirectX11笔记(五)--Direct3D渲染1--VERTICES AND INPUT LAYOUTS
原文:DirectX11笔记(五)--Direct3D渲染1--VERTICES AND INPUT LAYOUTS 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.c ...
- 学习JDK1.8集合源码之--TreeSet
1. TreeSet简介 TreeSet是Set的实现类之一,是不可重复集合,非线程安全的. TreeSet是SortedSet的唯一实现类,实现了元素的自动排序,排序不是以插入的顺序排序,而是默认以 ...
- Direct2D 第2篇 绘制椭圆
原文:Direct2D 第2篇 绘制椭圆 #include <windows.h> #include <d2d1.h> #include <d2d1helper.h> ...
- 虚幻UE4的后处理特效介绍 http://www.52vr.com/thread-31215-1-1.html
转载 虚幻UE4提供了后处理特效的功能,可以实现景深,光溢出,色调调整,饱和度等等.要使用虚幻4的后处理,就一定要用到PostProcessVolumn,这是一种特殊的体积,可以放置在场景中的任何位置 ...
- php rmdir使用递归函数删除非空目录的方法
php rmdir()函数 rmdir ― 删除空目录 语法: bool rmdir ( string $dirname [, resource $context ] )尝试删除 dirname 所指 ...