题意

一个整数数列,多次询问某段区间[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. Zuul【限流】

    在项目中,大部分都会使用到hyrtrix做熔断机制,通过某个预定的阈值来对异常流量进行降级处理,除了做服务降级以外,还可以对服务进行限流,分流,排队等. 当然,zuul也能做到限流策略,最简单的方式就 ...

  2. yzoj 2377 颂芬梭哈 题解

    题意 Alice 和 Mukyu 最近偶然得到了一本写有一种叫做梭哈的扑克游戏的规则的说明书(名为<C████████nd>,中间部分被涂掉了),据其所述,梭哈是一种使用黑桃.红心.梅花. ...

  3. 剑指offer27:按字典序打印出该字符串中字符的所有排列

    1 题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入描述: ...

  4. csdn博客整理

    @TOC 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页.如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown ...

  5. Linux大道——博客目录

      Linux基础 第一章 计算机基础 计算机基础 网络基础 第二章 Linux基础

  6. c语言实现串

    串  (string)是由零个或者多个字符组成的有限序列,又称字符串 一般表示为 S=“ a1 a2 a3 a4  . . . . . an”    其中S 是串名,双引号串起来的是串值,(有些书用单 ...

  7. 『Python基础练习题』day05

    # 请将列表中的每个元素通过 "_" 链接起来. users = ['毛利兰', '柯南', '怪盗基德'] # 请将元组 v1 = (11, 22, 33) 中的所有元素追加到列 ...

  8. Go语言学习笔记(9)——接口类型

    接口 Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口. /* 定义接口 */ type interface_name in ...

  9. 小贴士--java篇

    1. java:  “.”和“|”都是转义字符,必须得加"\\" 2.java :如果在一个字符串中有多个分隔符,可以用“|”作为连字符,比如:“acount=? and uu = ...

  10. 原生 js 录屏功能

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...