题意

一个整数数列,多次询问某段区间[li,ri][l_i,r_i][li​,ri​]内,选出若干个长度为LLL且不相交的连续段使选出来的数和最大。

分析

首先想朴素的区间DPDPDP

设f[i][j]f[i][j]f[i][j]表示区间[i,j][i,j][i,j]的答案。O(n2)O(n^2)O(n2)转移比较显然。

f[i][j]=max(f[i][j−1],f[i][j−L]+∑k=j−L+1ja[j])f[i][j]=max(f[i][j-1],f[i][j-L]+\sum_{k=j-L+1}^ja[j])f[i][j]=max(f[i][j−1],f[i][j−L]+k=j−L+1∑j​a[j])

为了方便我们把∑k=j−L+1ja[j]\sum_{k=j-L+1}^ja[j]∑k=j−L+1j​a[j]记作b[j]b[j]b[j],则

f[i][j]=max(f[i][j−1],f[i][j−L]+b[j])f[i][j]=max(f[i][j-1],f[i][j-L]+b[j])f[i][j]=max(f[i][j−1],f[i][j−L]+b[j])

空间和时间都会爆。然后想想多组询问怎么做。

发现L≤50L\le50L≤50,于是就有巧妙的分治做法。

对于区间[qL,qR][qL,qR][qL,qR],中点是midmidmid。我们把跨越midmidmid的询问拿出来求答案,没有跨越的分治下去,就变成了子问题。

那么对于一个询问[qL,qR][qL,qR][qL,qR],它的答案有两种情况。

  • 一种是没有选跨越midmidmid的连续段,那么就是[qL,mid][qL,mid][qL,mid]和[mid+1,qR][mid+1,qR][mid+1,qR]的答案加起来。
  • 另一种是跨越了midmidmid的。因为LLL很小我们枚举跨越midmidmid的一段在左边有多长,记作jjj,则右边长为L−jL-jL−j。那么有1<j<L,1<L−j<L1<j<L,1<L-j<L1<j<L,1<L−j<L。

    那么答案就是[qL,mid−j][qL,mid-j][qL,mid−j]的答案加上[mid+1+L−j,qR][mid+1+L-j,qR][mid+1+L−j,qR]的答案再加上中间一段的答案b[mid+L−j]b[mid+L-j]b[mid+L−j]

发现上面需要算答案的区间只有O(nL)O(nL)O(nL)级别的且连续,可以直接预处理。

然后就做完了。时间复杂度O(nLlog⁡n)O(nL\log n)O(nLlogn)。具体实现见代码。

CODE

#include <bits/stdc++.h>
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void rd(int &x) {
x = 0; char ch; int flg=1; while(!isdigit(ch=getc()))if(ch=='-')flg=-flg;
do x=x*10+ch-'0'; while(isdigit(ch=getc())); x *= flg;
}
const int MAXN = 100005;
const int MAXL = 55;
int n, m, L, a[MAXN], ans[MAXN];
struct query { int l, r, id; }q[MAXN], tmp1[MAXN], tmp2[MAXN];
int fL[MAXL][MAXN], fR[MAXL][MAXN];
inline void workL(int l, int r) {
for(int i = 0; i <= r-l && i < L; ++i) {
fL[i][r-i+1] = 0;
for(int j = r-i; j >= l; --j)
fL[i][j] = j+L-1 > r-i ? 0 : max(fL[i][j+1], fL[i][j+L] + a[j+L-1]);
}
}
inline void workR(int l, int r) {
for(int i = 0; i <= r-l && i < L; ++i) {
fR[i][l+i-1] = 0;
for(int j = l+i; j <= r; ++j)
fR[i][j] = j-L+1 < l+i ? 0 : max(fR[i][j-1], fR[i][j-L] + a[j]);
}
}
void cdq(int l, int r, int ql, int qr) {
if(ql > qr || r-l+1 < L) return;
int mid = (l + r) >> 1, it1 = 0, it2 = 0;
workL(l, mid); workR(mid+1, r);
for(int i = ql; i <= qr; ++i) {
if(q[i].r <= mid) tmp1[++it1] = q[i];
else if(q[i].l > mid) tmp2[++it2] = q[i];
else {
ans[q[i].id] = fL[0][q[i].l] + fR[0][q[i].r];
for(int j = max(1, L-(q[i].r-mid)); j <= mid-q[i].l+1 && j < L; ++j)
ans[q[i].id] = max(ans[q[i].id], (mid-j < l ? 0 : fL[j][q[i].l]) + (mid+L-j+1 > r ? 0 : fR[L-j][q[i].r]) + a[mid+L-j]);
}
}
for(int i = 1; i <= it1; ++i) q[ql+i-1] = tmp1[i];
for(int i = 1; i <= it2; ++i) q[ql+it1+i-1] = tmp2[i];
cdq(l, mid, ql, ql+it1-1); cdq(mid+1, r, ql+it1, ql+it1+it2-1);
}
int main () {
rd(n), rd(L);
for(int i = 1; i <= n; ++i) rd(a[i]), a[i] += a[i-1];
for(int i = n; i >= L; --i) a[i] -= a[i-L];
rd(m);
for(int i = 1; i <= m; ++i) rd(q[i].l), rd(q[i].r), q[i].id = i;
cdq(1, n, 1, m);
for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}

BZOJ 3636 教义问答手册 (分治)的更多相关文章

  1. BZOJ3636: 教义问答手册

    Description “汉中沃野如关中,四五百里烟蒙蒙.黄云连天夏麦熟,水稻漠漠吹秋风.”——摘自 黄裳<汉中行>“泉岭精神不朽,汉中诸球永生.”——摘自<泉岭精神创立者语录> ...

  2. 题解 「BZOJ3636」教义问答手册

    题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...

  3. BZOJ.4184.shallot(线段树分治 线性基)

    BZOJ 裸的线段树分治+线性基,就是跑的巨慢_(:з」∠)_ . 不知道他们都写的什么=-= //41652kb 11920ms #include <map> #include < ...

  4. [BZOJ 4025]二分图(线段树分治+带边权并查集)

    [BZOJ 4025]二分图(线段树分治+带边权并查集) 题面 给出一个n个点m条边的图,每条边会在时间s到t出现,问每个时间的图是否为一个二分图 \(n,m,\max(t_i) \leq 10^5\ ...

  5. BZOJ.4320.[ShangHai2006]Homework(根号分治 分块)

    BZOJ \(\mathbb{mod}\)一个数\(y\)的最小值,可以考虑枚举剩余系,也就是枚举区间\([0,y),[y,2y),[2y,3y)...\)中的最小值(求后缀最小值也一样)更新答案,复 ...

  6. BZOJ 1176 [Balkan2007]Mokia ——CDQ分治

    [题目分析] 同BZOJ2683,只需要提前处理s对结果的影响即可. CDQ的思路还是很清晰的. 排序解决一维, 分治时间, 树状数组解决一维. 复杂度是两个log [代码] #include < ...

  7. BZOJ 2683 简单题 ——CDQ分治

    [题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...

  8. BZOJ 3365 Distance Statistics 点分治

    这道题是一道点分治的题目,难度不大,可以拿来练手. 关键是对于找出来的重心的删除操作需要删掉这条边,这很重要. 还有每次找重心的时候,不但要考虑他的子节点的siz,还要考虑父节点的siz. 然后就A了 ...

  9. 【BZOJ 1468】Tree 点分治

    点分治$O(nlogn)$ 坚持到月考结束后新校就剩下我一个OIer,其他人早已停课了,老师估计懒得为我一个人开机房门,让我跟班主任说了一声,今晚就回到了老校,开始了自己都没有想到会来的这么早的停课生 ...

随机推荐

  1. 第五章 模块之 logging、copy、re

    5.12 logging 日志模块 报警等级 CRITICAL = 50 # 最高FATAL = CRITICALERROR = 40WARNING = 30WARN = WARNINGINFO = ...

  2. C++打印杨辉三角形

    #include <iostream> #include <iomanip> #include <Windows.h> using namespace std; # ...

  3. 01 IO流(一)—— 流的概念、File类

    1 流的概念理解(重要) 理解流的概念非常重要. 流,就是程序到数据源或目的地的一个通道. 我们把这个通道实例化得到一个具体的流,相当于一个数据传输工具,它可以在程序与资源之间进行数据交换. 换言之, ...

  4. PAT(B) 1051 复数乘法(Java)

    题目链接:1051 复数乘法 (15 point(s)) 参考博客:1051 复数乘法(PAT 乙级 C++实现)-guangjinzheng 题目描述 复数可以写成 (A+Bi) 的常规形式,其中 ...

  5. WUSTOJ 1308: 采药(Java)动态规划-01背包

    题目链接:

  6. 理解atoi()函数

    atoi函数 功能:字符串转化为整型数 #include <iostream> using namespace std; int atoi_my(const char *str) { ; ...

  7. dotnet Core学习之旅(三):创建项目

    [重要:文中所有外链不能确保永久有效]>创建解决方案 在VSCode上,可以使用来自开源力量的vscode扩展vscode-solution-explorer来增强VSCode对.NET项目的支 ...

  8. Spring Boot集成Mybatis完整实例

    步骤: 添加Mybatis依赖: 添加数据库依赖: 配置属性文件: (具体的属性名称可以在jar包中找到) 内容: 建表sql: Mapper文件的头: 集成Mybatis的配置文件中的具体内容可以在 ...

  9. nodejs中使用mongodb

    /** * 使用mongodb存储数据 * 1 首先安装mongodb nodejs插件 npm install mongodb --save-dev * 2 安装express (非必须) * * ...

  10. 在ubuntu 16.04 的vm中添加新网卡,同一网段不同ip

    在ubuntu 16.04 的vm中添加新网卡,同一网段不同ip 来源 https://blog.51cto.com/744478/2083672 在ubuntu 16.04 的vm中新加了一块网卡, ...