BZOJ 3636 教义问答手册 (分治)
题意
一个整数数列,多次询问某段区间[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∑ja[j])
为了方便我们把∑k=j−L+1ja[j]\sum_{k=j-L+1}^ja[j]∑k=j−L+1ja[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(nLlogn)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 教义问答手册 (分治)的更多相关文章
- BZOJ3636: 教义问答手册
Description “汉中沃野如关中,四五百里烟蒙蒙.黄云连天夏麦熟,水稻漠漠吹秋风.”——摘自 黄裳<汉中行>“泉岭精神不朽,汉中诸球永生.”——摘自<泉岭精神创立者语录> ...
- 题解 「BZOJ3636」教义问答手册
题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...
- BZOJ.4184.shallot(线段树分治 线性基)
BZOJ 裸的线段树分治+线性基,就是跑的巨慢_(:з」∠)_ . 不知道他们都写的什么=-= //41652kb 11920ms #include <map> #include < ...
- [BZOJ 4025]二分图(线段树分治+带边权并查集)
[BZOJ 4025]二分图(线段树分治+带边权并查集) 题面 给出一个n个点m条边的图,每条边会在时间s到t出现,问每个时间的图是否为一个二分图 \(n,m,\max(t_i) \leq 10^5\ ...
- BZOJ.4320.[ShangHai2006]Homework(根号分治 分块)
BZOJ \(\mathbb{mod}\)一个数\(y\)的最小值,可以考虑枚举剩余系,也就是枚举区间\([0,y),[y,2y),[2y,3y)...\)中的最小值(求后缀最小值也一样)更新答案,复 ...
- BZOJ 1176 [Balkan2007]Mokia ——CDQ分治
[题目分析] 同BZOJ2683,只需要提前处理s对结果的影响即可. CDQ的思路还是很清晰的. 排序解决一维, 分治时间, 树状数组解决一维. 复杂度是两个log [代码] #include < ...
- BZOJ 2683 简单题 ——CDQ分治
[题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...
- BZOJ 3365 Distance Statistics 点分治
这道题是一道点分治的题目,难度不大,可以拿来练手. 关键是对于找出来的重心的删除操作需要删掉这条边,这很重要. 还有每次找重心的时候,不但要考虑他的子节点的siz,还要考虑父节点的siz. 然后就A了 ...
- 【BZOJ 1468】Tree 点分治
点分治$O(nlogn)$ 坚持到月考结束后新校就剩下我一个OIer,其他人早已停课了,老师估计懒得为我一个人开机房门,让我跟班主任说了一声,今晚就回到了老校,开始了自己都没有想到会来的这么早的停课生 ...
随机推荐
- Python 绘图库Matplotlib入门教程
0 简单介绍 Matplotlib是一个Python语言的2D绘图库,它支持各种平台,并且功能强大,能够轻易绘制出各种专业的图像. 1 安装 pip install matplotlib 2 入门代码 ...
- 多线程(8) — ThreadLocal
ThreadLocal是一个线程的局部变量,也就是只有当前线程可以访问,是线程安全的.为每一个线程分配不同的对象,需要在应用层面保证ThreadLocal只起到简单的容器作用. ThreadLocal ...
- (一)Shiro,久闻其名,而今初相识
文章目录 shiro简介 功能介绍 从外部看 Shiro 架构 从内部看 Shiro 架构 多说一句,在学习shiro之前,我觉得应该先用 filter ,自己动手写过粗粒度的权限系统,而不要一上来就 ...
- C++中深拷贝与浅拷贝
浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B.这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指 ...
- go select 的default
当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行. 为了非阻塞的发送或者接收,可使用 default 分支: select { case i := <-c: ...
- Thread interrupted() 线程的中断
问题: 1.线程的中断方式. 2.为什么中断阻塞中的线程,会抛出异常. 代码示例: package com.hdwl.netty; public class ThreadInterrupted { p ...
- C++ 异步编程:Boost.Asio
Boost.Asio 是一个用于网络和低级 I/O 编程的跨平台 C++ 库,它使用现代 C++ 方法为开发人员提供一致的异步模型 一个异步使用计时器的样例 #include <iostream ...
- 无法解析的外部符号 ___argc nafxcw.lib(appcore.obj)
参考 https://blog.csdn.net/wfree/article/details/44171157 MFC的使用: 由"在静态库中使用 MFC" 改成 "在共 ...
- mysql 系统变量
show variables; ---------------------------------+-------------------------------------------------- ...
- 微信小程序 swiper 组件坑
swiper 组件高度被限制为150px了,所以内容无法撑开. 解决办法 给这组件重新设置个高度,然后在把里面的图片设置为自动适应容器大小.图片模式设置为 宽度不变 自动适应高度 <swiper ...