P1295 [TJOI2011]书架


本题思路比较好想(对我来说不是),但代码细节很多,奈何洛谷的题解只有思路,然后就是

没有丝毫解释的代码,让人看起来很头疼(~~ 尤其是像我这样的蒟蒻~~),所以便打算写一篇带

注释的题解;

题目大意

题目链接

给出一个长度为 n 的序列 h,请将 h 分成若干段,满足每段数字之和都不超过 m,最小化每段的

最大值之和。

解题思路

我们不难想到30分的n2做法,但期望得分30(据说实际有50)

我们定义f [ i ]为1 - i分段后的最大值和

可得dp方程

f[i]=f[j]+max(a[j+1]...a[i])(s[i]-s[j]<=m)

我们考虑对其进行优化

首先我们计算到i时,有 j< k < i(s[i]-s[j]<=m)

假设a[j] ~a[i] 的最大值与 a[k]~a[i]的最大值相等

那么 j ,k 实际上对f[i]的值产生的贡献是由f[j] f[k]决定的,那么,又f的单调性

即 f[j]<=f[k] (j<k),我们知道选择f[j]一定比选择f[k]的值更优秀

所以我们只需在和值小于等于m的范围内根据到i 的最大值大小分段,使每一段到i的最大值都

相等,我们只需计算出每一段中最小下标对应的a加上该段的max,然后在所有段中取得最小

便可以更新出答案将序列分段我们利用单调队列来完成,维护没一个值向左的最远影响(实际便是

到该点以该点权值为最大值的最小下标) ,利用线段树将每一段的值加载到线段树

上,维护合法的最小值,即最终答案即可,时间复杂度为线段树的时间复杂度,即O(nlogn);

这样我们就可以在规定时间内算出最优解了

代码部分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string> using namespace std;
const int maxn=100005;
//快读
inline int read(){
int ret=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-f;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
ret=ret*10+(ch^'0');
ch=getchar();
}
return ret*f;
}
// 我们用line来维护单调队列数组中下标在a数组中的数值
// w为该单调队列下标在a数组中对应的下标
// v为对应的长度,f 为该值可以影响的最大范围的值的a数组下标
struct node{
int w,v,f;
}lin[maxn];
int tre[5*maxn];//线段树,实际开4倍就够了
int n,m;
int head;
int tail;
int a[maxn];
//将每一段值加载到线段树上
//为了因为最终答案为最小值,所以我们线段是维护的也是最小值
void plu(int ro,int l,int r,int v,int nu){
if(l==r){
tre[ro]=v;
return ;
}
int mid=(l+r)>>1;
if(mid>=nu){
plu(ro*2,l,mid,v,nu);
}
else{
plu(ro*2+1,mid+1,r,v,nu);
}
tre[ro]=min(tre[ro*2],tre[ro*2+1]);
return ;
}
//查找最小值
int sea(int ro,int l,int r,int lf,int rf){
if(l==r){
return tre[ro];
}
if(lf<=l&&rf>=r){
return tre[ro];
}
int mid=(l+r)>>1;
int ans=0x3f3f3f3f;//理应给最大值,之前给小了,查了半天
if(lf<=mid){
ans=sea(ro*2,l,mid,lf,rf);
}
if(rf>=mid+1){
ans=min(ans,sea(ro*2+1,mid+1,r,lf,rf));
}
return ans;
}
int t;
int h=1;
int f[maxn];
int s;
int last[maxn];//用于存放以该点为结尾<=m的序列的头元素下标
int maxx=-1;
int main(){
freopen("a.in","r",stdin);
n=read();
m=read();
for(int i=1;i<=n;i++){
a[i]=read();
// cout<<a[i]<<endl;
} // cout<<n<<" "<<m;
head=1,tail=0;
//先进行预处理
while(t<n&&a[t+1]+s<=m){
t++;
s+=a[t];
last[t]=1;
maxx=max(maxx,a[t]);
//cout<<t;
f[t]=maxx;
while(tail>0&&a[lin[tail].w]<=a[t]){
tail--;
}
tail++;
lin[tail].w=t;
lin[tail].v=a[t];
lin[tail].f=lin[tail-1].w;
// cout<<lin[tail].f<<endl;
plu(1,1,n,f[lin[tail-1].w]+a[t],tail);//将该点以本身为最值的f更新到线段树上
//tail++; }
//维护每个到i的和<=m的最远下标
for(int i=t+1;i<=n;i++){
s+=a[i];
while(s>m){
s-=a[h];
h++;
}
last[i]=h;
} for(int i=t+1;i<=n;i++){
while(lin[head].w<last[i]&&head<=tail){
lin[head].w=0;
head++;
}//保证head在合法范围内即不能大于m
if(lin[head].f<last[i]-1){
lin[head].f=last[i]-1;
plu(1,1,n,f[last[i]-1]+lin[head].v,head);//将更新后的head加载到线段树上至于为什么要更新,因为head下的值已经发生变化
}
while(head<=tail&&a[i]>=lin[tail].v){
tail--;
}
tail++;
lin[tail].w=i;
lin[tail].v=a[i];
lin[tail].f=max(lin[tail-1].w,last[i]-1);
plu(1,1,n,f[lin[tail].f]+lin[tail].v,tail);//加载然后搜索即可
f[i]=sea(1,1,n,head,tail);//
//tail++;
}
cout<<f[n];
return 0;
}

完结撒花

以小紬结尾

P1295 [TJOI2011]书架 线段树优化dp,单调栈的更多相关文章

  1. 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点

    容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...

  2. Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 学 whk 时比较无聊开了道题做做发现是道神题( 介绍一种不太一样的做法,不观察出决策单调性也可以做. 首先一个很 trivial 的 o ...

  3. 2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP)

    2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP) https://www.luogu.com.cn/problem/P1848 题意: 当农夫约翰闲 ...

  4. Codeforces Round #426 (Div. 2) D 线段树优化dp

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

  5. BZOJ2090: [Poi2010]Monotonicity 2【线段树优化DP】

    BZOJ2090: [Poi2010]Monotonicity 2[线段树优化DP] Description 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k]. ...

  6. [AGC011F] Train Service Planning [线段树优化dp+思维]

    思路 模意义 这题真tm有意思 我上下楼梯了半天做出来的qwq 首先,考虑到每K分钟有一辆车,那么可以把所有的操作都放到模$K$意义下进行 这时,我们只需要考虑两边的两辆车就好了. 定义一些称呼: 上 ...

  7. 【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

    题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a varian ...

  8. POJ 2376 Cleaning Shifts (线段树优化DP)

    题目大意:给你很多条线段,开头结尾是$[l,r]$,让你覆盖整个区间$[1,T]$,求最少的线段数 题目传送门 线段树优化$DP$裸题.. 先去掉所有能被其他线段包含的线段,这种线段一定不在最优解里 ...

  9. 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$

    正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...

随机推荐

  1. 涨见识!Java String转int还有这种写法

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题 ...

  2. ElasticSearch实战系列八: Filebeat快速入门和使用---图文详解

    前言 本文主要介绍的是ELK日志系统中的Filebeat快速入门教程. ELK介绍 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是 ...

  3. Azure认知服务之使用墨迹识别功能识别手写汉字

    前面我们使用Azure Face实现了人脸识别.使用Azure表格识别器提取了表格里的数据.这次我们试试使用Azure墨迹识别API来对笔迹进行识别. 墨迹识别 墨迹识别器认知服务提供基于云的 RES ...

  4. iOS多线程之GCD、OperationQueue 对比和实践记录

    [toc] 简介      在计算的早期,计算机可以执行的最大工作量是由 CPU 的时钟速度决定的.但是随着技术的进步和处理器设计的紧凑化,热量和其他物理约束开始限制处理器的最大时钟速度.因此,芯片制 ...

  5. new操作符创建对象的四个步骤

    new操作符创建对象可以分为以下四个步骤: 创建一个空对象 将所创建对象的__proto__属性值设为构造函数的prototype的属性值 执行构造函数中的代码,构造函数中的this指向该对象 返回对 ...

  6. jmeter http并发测试时报错

    错误信息如下:jmeter Response code: Non HTTP response code: java.net.URISyntaxException 网上收了一大堆,都没法解决 我的用到了 ...

  7. Vue根据条件添加 click 事件

    方式一:在绑定事件中直接添加标示量clickFlag <div @click="clickFlag && addGoodsHandler()"> XXX ...

  8. vue-cli根据不同的命令自动切换不同环境地址

    前言 我们再使用vue脚手架开发项目时,不可避免的涉及到多个环境来回切换接口调用地址的问题,在开发环境中可能会通过 ip 来访问后台接口,但是当项目上线后就要把对用的接口地址换成生产环境的地址,肯定不 ...

  9. gdb我在我本机上p不了,在别人机子上可以

    gdb我在我本机上p不了,在别人机子上可以,不知道什么 (gdb) p EventFlow->devicetypeThere is no member or method named devic ...

  10. 轻轻松松学CSS:float

    float属性,会使元素向左或向右移动,其周围的元素也会重新排列.float不仅自己飘忽不定,还对周围元素有影响,这种影响力不容小觑.他捉摸不定(浮动规律不好把握),他干涉他国内政(对周围元素有影响) ...