P8037 [COCI2015-2016#7] Prokletnik 题解

news/2025/1/8 16:02:12/文章来源:https://www.cnblogs.com/kkxacj/p/18657426

题意

定义一个区间 $l,r$ 为好的当且仅当最小值为 $a_l$ 且最大值为 $a_r$最大值为 $a_l$ 且最小值为 $a_r$。

我们先考虑最小值为 $a_l$ 且最大值为 $a_r$ 的,另一个我们翻转过来在搞一遍就好了。

先考虑将询问离线按 $r$ 排序,容易发现,对于每个右端点 $r$ 对应的合法左端点是一段单调递增的数,若我们将 $r$ 从小到大跑,最后一次给左端点 $l$ 赋值的右端点 $r$ 一定是最优的,这很显然,因为固定了 $l$,$r$ 越大,长度越大。

然后由于对应的合法左端点是一段单调递增的数,可以用单调栈存,考虑开两个线段树分别维护单调栈里的和不在单调栈里的,这里注意,由于 $r$ 前面可能有比 $a_r$ 大的数,所以要二分判单调栈内第一个合法位置在哪。

由于每个数最多进栈出栈一次,所以时间是可以保证的,线段树则需要能满足单点覆盖和区间加,区间求最大值

最后求答案,直接二分栈里第一个下标大于等于 $q_l$ 的数,同时求不在单调栈里的 $q_l$ 到 $q_r$ 的最大值就行。

然后在反着做一遍就完了,代码有部分注释方便理解。

code

#include<bits/stdc++.h>
#define mid ((L+R)>>1)
#define ls (p<<1)
#define rs ((p<<1)+1) 
using namespace std;
namespace IO
{template<typename T>void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}template<typename T,typename... Args>void read(T &_x,Args&...others){Read(_x);Read(others...);}const int BUF=20000000;char buf[BUF],to,stk[32];int plen;#define pc(x) buf[plen++]=x#define flush(); fwrite(buf,1,plen,stdout),plen=0;template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 1e6+10;
int n,q,a[N],lg[N],st[N][22],ans[N],fg[N<<2],bj[N<<2],cnt,t[N],cnt1,l,r,m1d,sum,o,o1;
struct w
{int l,r,id;
}b[N];
struct w1
{int mx;
}c[N<<2][2];
inline bool cmp(w x,w y){return x.r < y.r;}
void build(int p,int L,int R,int id)
{fg[p] = bj[p] = 0;if(L == R){if(id == 0) c[p][id].mx = -l+1;//由于是求区间长度,提前减去左端点就好了 return;}build(ls,L,mid,id),build(rs,mid+1,R,id); c[p][id].mx = max(c[ls][id].mx,c[rs][id].mx);
}
inline void push_up1(int p,int id,int L,int R)
{if(id == 1) c[ls][id].mx = -t[L]+1,c[rs][id].mx = -t[R]+1;else c[ls][id].mx = -L+1,c[rs][id].mx = -R+1;bj[ls] = bj[rs] = 0;fg[ls] = fg[rs] = 1;fg[p] = 0;
}
inline void push_up(int p,int id)
{c[ls][id].mx += bj[p],c[rs][id].mx += bj[p];bj[ls] += bj[p],bj[rs] += bj[p];bj[p] = 0;
}
void change(int p,int l,int r,int k,int id,int L,int R)
{if(l <= L && R <= r){if(k == -1) {if(id == 1) c[p][id].mx = -t[L]+1,bj[p] = 0,fg[p] = 1;//注意这是栈里的数,赋的不一样 else c[p][id].mx = -L+1,bj[p] = 0,fg[p] = 1;//clear }else {if(id == 1) c[p][id].mx += k,bj[p] += k;else c[p][id].mx = max(c[p][id].mx,k);}return;} if(fg[p] && id == 1) push_up1(p,id,L,mid+1);if(bj[p] && id == 1) push_up(p,id);if(l <= mid) change(ls,l,r,k,id,L,mid);if(mid < r) change(rs,l,r,k,id,mid+1,R);c[p][id].mx = max(c[ls][id].mx,c[rs][id].mx);
}
void query(int p,int l,int r,int id,int L,int R)
{if(l <= L && R <= r){sum = max(sum,c[p][id].mx);return;}if(fg[p] && id == 1) push_up1(p,id,L,mid+1);if(bj[p] && id == 1) push_up(p,id);if(l <= mid) query(ls,l,r,id,L,mid);if(mid < r) query(rs,l,r,id,mid+1,R);
}
signed main()
{read(n); build(1,1,n,0),build(1,1,n,1);for(int i = 2;i <= n;i++) lg[i] = lg[i/2]+1;for(int i = 1;i <= n;i++) read(a[i]),st[i][0] = a[i];read(q);for(int i = 1;i <= q;i++) read(b[i].l),read(b[i].r),b[i].id = i;sort(b+1,b+1+q,cmp);for(int i = 1;i <= lg[n];i++)for(int j = 1;j+(1<<i)-1 <= n;j++)st[j][i] = max(st[j][i-1],st[j+(1<<(i-1))][i-1]);//st表求出最大值 cnt = 1;for(int i = 1;i <= n;i++){while(cnt1 && a[t[cnt1]] > a[i]) {sum = 0; query(1,cnt1,cnt1,1,1,n);change(1,t[cnt1],t[cnt1],sum,0,1,n);//清空后赋值 cnt1--;}t[++cnt1] = i;int k = lg[i-1];l = 1,r = cnt1,o = cnt1;while(l <= r)//二分出合法位置 {m1d = (l+r)/2;int k = lg[i-t[m1d]+1];o1 = max(st[t[m1d]][k],st[i-(1<<k)+1][k]);//如果这一段最大值=a_r,这可行 if(o1 != a[i]) l = m1d+1;else r = m1d-1,o = m1d;}change(1,o,cnt1,-1,1,1,n);//清空,应为要给新的右端点了 change(1,o,cnt1,i,1,1,n);while(cnt <= q && b[cnt].r == i)//处理右端点为i的询问 {sum = 0; query(1,b[cnt].l,b[cnt].r,0,1,n);//先求出栈外的 l = 1,r = cnt1,o = 0;while(l <= r)//二分出合法位置 {m1d = (l+r)/2;if(b[cnt].l <= t[m1d]) o = m1d,r = m1d-1;else l = m1d+1;}if(o) query(1,o,cnt1,1,1,n);ans[b[cnt].id] = sum; cnt++;}}//从大到小for(int i = 1;i <= lg[n];i++)//下半部分思路同上,就左端点变成最大了for(int j = 1;j+(1<<i)-1 <= n;j++)st[j][i] = min(st[j][i-1],st[j+(1<<(i-1))][i-1]); build(1,1,n,0),build(1,1,n,1); cnt1 = 0,cnt = 1;for(int i = 1;i <= n;i++){while(cnt1 && a[t[cnt1]] < a[i]) {sum = 0; query(1,cnt1,cnt1,1,1,n);change(1,t[cnt1],t[cnt1],sum,0,1,n);//清空后赋值 cnt1--;}t[++cnt1] = i;int k = lg[i-1];l = 1,r = cnt1,o = cnt1;while(l <= r){m1d = (l+r)/2;int k = lg[i-t[m1d]+1];o1 = min(st[t[m1d]][k],st[i-(1<<k)+1][k]);if(o1 != a[i]) l = m1d+1;else r = m1d-1,o = m1d;}change(1,o,cnt1,-1,1,1,n);change(1,o,cnt1,i,1,1,n);while(cnt <= q && b[cnt].r == i){sum = 0; query(1,b[cnt].l,b[cnt].r,0,1,n);l = 1,r = cnt1,o = 0;while(l <= r){m1d = (l+r)/2;if(b[cnt].l <= t[m1d]) o = m1d,r = m1d-1;else l = m1d+1;}if(o) query(1,o,cnt1,1,1,n);ans[b[cnt].id] = max(ans[b[cnt].id],sum); cnt++;}} for(int i = 1;i <= q;i++) print(ans[i]),pc('\n');flush();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/865297.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大语言模型提示技巧(六)-文本转换

大语言模型是基于自然语言的人工智能,所以它在语言上的表现相当出色,使用大语言模型进行可以进行诸如翻译、语气转换、润色、语言评价、扩写、润色等语言处理,对于日常文字工作,它是一名合格甚至优秀的私人秘书。 (一)翻译 在不同语言之间进行翻译是众多大语言模型都支持…

Unreal Engine 5 课程记录 蓝图部分(非教程)

学习课程:Unreal Engine 5 – Full Course for Beginners 非教程,仅学习记录及碎碎念,学完感觉UE就非常的像预制菜,简简单单就能有非常好的效果(非常に新鲜で、非常に美味しい!),但装料太足了要想拆分明白用料和流程又会比较复杂!Creating Levels 创建基本关卡,几个组…

Unreal Engine 5 课程笔记 蓝图部分

学习课程:Unreal Engine 5 – Full Course for Beginners 非教程,仅学习记录及碎碎念,学完感觉UE就非常的像预制菜,简简单单就能有非常好的效果(非常に新鲜で、非常に美味しい!),但装料太足了要想拆分明白用料和流程又会比较复杂!Creating Levels 创建基本关卡,几个组…

通过修改格式灵活运用百度搜索

1、“+”加号 —— 强制包含关键词 在关键词的前面使用加号,也就等于告诉搜索引擎该单词必须出现在搜索结果中的网页上。 例如:在搜索引擎中输入“+电脑+电话+传真”就表示要查找的内容必须要同时包含“电脑、电话、传真”这三个关键词。 2、“-”非——消除无关性 逻辑“非”…

Arm主板与x86主板的区别

在当今数字化迅猛发展的时代,计算机硬件的架构选择对于系统的性能、能效以及适用性具有至关重要的影响。Arm架构与x86架构是目前主流的两种计算机体系结构,各自在个人计算机、服务器、嵌入式系统及移动设备等领域中占据了重要地位。本文旨在探讨Arm主板与x86主板之间的区别,…

E. Beautiful Array(题解)

原题链接: https://codeforces.com/problemset/problem/1986/E 思路: 排序,取模, 思维 关于操作:ai=ai+k; 若要使a1+m1*k==a2+m2*k; 则当a1, a2满足a1%k==a2%k,a1,a2可以满足a1+m1*k==a2+m2*k;并在需要(|a1-a2|)/k次操作。将a数组取模后,用vector分别储存, a1和a2相差越…

Python语言中进程、线程、协程执行效率分析

python语言中进程、线程、协程执行效率比较。 问题:python语言中 进程、线程、协程执行速度哪个最快? 在Python中,进程、线程和协程的执行速度不能简单地进行比较,因为它们的性能取决于多种因素,包括任务类型、I/O操作、CPU密集型计算、操作系统调度策略以及Python解释器的…

CICD Day4、Jenkins主从架构

Jenkins主从架构(Master-Slave)是一种分布式架构,主节点负责管理项目配置、任务调度和监控,从节点用于执行具体的构建任务。Jenkins主从架构如下图所示当项目触发构建时,主节点将任务分配到某个从节点,从节点根据项目配置执行一系列操作,如拉取代、代码编译、部署到目标…

如何进一步做好信息收集

如何进一步做好信息收集 前言 前面一节介绍了一些信息收集的网站和工具,今天主要介绍一下如何进行半自动化的信息收集,全自动化的信息收集容易出现一些脏数据,而完全手工进行信息收集速率又太低,所以为了提高速率,我们需要充分利用一些脚本和工具 WHOIS半自动化收集 通过W…

链路诊断最佳实践:1 分钟定位错慢根因

面向生产应用“错”、“慢”两大风险,通过链路追踪及其关联数据、跨域实体关系和大模型算法,实现错慢请求智能根因定位,提升系统稳定性和运维效率。线上应用风险主要分为“错”、“慢”两大类。其中“错”的原因通常是程序运行不符合预期,比如 JVM 加载了错误版本的类实例,…

SwanLab最全使用教程:看这篇就够了

SwanLab是一个用于可视化和监控深度学习模型的工具。本文介绍了SwanLab的安装、启动和使用方法,并提供了参考链接。前言 机器学习通常涉及在训练期间可视化和度量模型的性能。 有许多工具可用于此任务。 在本文中,我们将重点介绍 SwanLab 开源工具,它可以服务于各种深度学习…

维修ABB IRB6700机器人的平衡缸3HAC043477出现异响

当ABB IRB6700机器人的平衡缸3HAC043477出现异响时,可能需要进行内部零件的检查和更换。以下是一些建议的步骤:1、检查IRB6700机械臂平衡缸的密封性:确保平衡缸的密封性良好,没有气体泄漏。如果发现有气体泄漏,可能需要更换密封件。2、检查活塞和缸体的磨损情况:如果活塞…