首页 > 其他分享 >Zbox loves stack

Zbox loves stack

时间:2023-07-21 23:44:15浏览次数:36  
标签:ri le int void tr stack loves Zbox define

Zbox loves stack

题意

有 \(n\) 个栈,\(q\) 次操作,\(3\) 种操作。
1.\([l,r]\) 之间的栈全部加入一个数 \(k\)。
2.\([l,r]\) 之间的栈全部弹出栈顶。
3.第 \(s\) 个栈中的第 \(k\) 个元素,栈顶为第一个元素,没有则输出 Error
\(n \leq 10^6,q \leq 10^5\)

题解

考试的时候想到了树套树的写法,但是因为不会可持久化平衡树寄掉了。
实际上是有 \(O(qlogn)\) 的写法的。
直接在线写标记下传感觉很难,所以考虑离线。

我们将先考虑我们如果知道一个栈的操作序列,将插入看做 \(1\),删除看做 \(-1\)。
显然第 \(k\) 个元素就是最后一个后缀和为 \(k\) 的位置。

我们要怎么知道一个栈的操作序列?
用扫描线的方法把删除,添加和询问都挂在栈上,从后往前扫描。

光挂到栈上不行,还要知道操作之间的顺序。
考虑以时间为下标建立线段树,每个操作加入到对应的时间即可。
询问就是询问的时间这一个前缀中最后一个值为 \(k\) 的后缀和。

考虑到同时有 \(1,0,-1\) 的存在,不能直接在线段树上二分。
但是注意到一段后缀移动一个位置和的影响只会是 \(0,1,-1\),根据这个我们可以得到一个结论:
对于一段区间的后缀最大值 \(mx\),最小值 \(mi\),若 \(mi\leq k \leq mx\) ,则目标位置一定在这个区间中。
结论还是比较经典的,根据这个结论直接在线段树上做就行了,然后这道题就结束了,时间复杂度 \(O(qlogn)\)。

code

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc( '\n' )
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
    x = 0 ;rg int fk(0) ; rg char c(gc()) ;
    while(!isdigit(c)) fk |= (c=='-'),c = gc() ;
    while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
    x = (fk?-x:x) ;
    read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
    if(x < 0) pc('-'),x = -x ;
    do stk[++tp] = x%10,x /= 10 ; while(x) ;
    while(tp) pc(stk[tp--]^48) ; print(p...) ;
}
const int N = 1e6+5 ;
int n,q ;
vector<int>e[N] ;
void Solve1()
{
	while(q--)
	{
		int op,le,ri,k ; read(op,le,ri) ;
		if(op == 0) 
		{
			read(k) ;
			FOR(i,le,ri,1) e[i].pb(k) ;
		}
		if(op == 1)
		{
			FOR(i,le,ri,1) if(e[i].size())
				e[i].pop_back() ;
 		}
		if(op == 2) 
		{
			if(e[le].size() < ri) puts("Error") ;
			else print(e[le][e[le].size()-ri]),enter ;
		}
	}
}
struct Node
{
	int l,r,g ;
	vector<int>p ;
}tr[N<<2] ;
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define g(x) tr[x].g
#define mid (tr[x].l+tr[x].r>>1)
void Build(int x,int le = 1,int ri = n)
{
	tr[x].l = le,tr[x].r = ri ; if(le == ri) return ;
	Build(ls(x),le,mid),Build(rs(x),mid+1,ri) ;
}
inline void f(int x,int k,vector<int>&p)
{
	while(k && tr[x].p.size()) tr[x].p.pop_back(),--k ;
	g(x) += k ; for(auto v:p) tr[x].p.pb(v) ;
}
inline void down(int x)
{
	f(ls(x),g(x),tr[x].p),f(rs(x),g(x),tr[x].p),g(x) = 0,tr[x].p.clear() ;
}
void Modify(int x,int le,int ri,int k)
{
	if(tr[x].l >= le && tr[x].r <= ri) {tr[x].p.pb(k) ; return ;}
	down(x) ; if(le <= mid) Modify(ls(x),le,ri,k) ; if(mid < ri) Modify(rs(x),le,ri,k) ;
}
void Erase(int x,int le,int ri)
{
	if(tr[x].l >= le && tr[x].r <= ri)
	{
		if(tr[x].p.size()) tr[x].p.pop_back() ;
		else ++g(x) ; return ;
	}
	down(x) ; if(le <= mid) Erase(ls(x),le,mid) ; if(mid < ri) Erase(rs(x),mid+1,ri) ;
}
int Query(int x,int k,int l)
{
	if(tr[x].l == tr[x].r) return tr[x].p.size()>=l?tr[x].p[tr[x].p.size()-l]:-1 ;
	down(x) ; return (k <= mid) ? Query(ls(x),k,l) : Query(rs(x),k,l) ;
}
void Solve2()
{
	Build(1) ;
	while(q--)
	{
		int op,le,ri,w ; read(op,le,ri) ;
		if(op == 0) read(w),Modify(1,le,ri,w) ;
		if(op == 1) Erase(1,le,ri) ;
		if(op == 2)
		{
			int p = Query(1,le,ri) ;
			if(~p) print(p),enter ;
			else puts("Error") ;
		}
	}
}
signed main()
{
    // freopen(".in","r",stdin) ;
    // freopen(".out","w",stdout) ;
    read(n,q) ;
    if(n <= 5000 && q <= 5000) Solve1() ; 
    else Solve2() ;
	return 0 ;
}

...

这道题难吗,感觉也不难。
考试的时候也想到过转化为 \(1,-1\) 这种操作,但是在得到操作序列个顺序卡住了,然后就寄了。
这道题首先用离线的方式得到了操作序列,然后又以时间为下标建立了线段树的到顺序,消掉一维。
其实还是很套路的,看来还是我太菜了/kk。

标签:ri,le,int,void,tr,stack,loves,Zbox,define
From: https://www.cnblogs.com/snow-panther/p/17572635.html

相关文章

  • [极客大挑战 2019]LoveSQL
    [极客大挑战2019]LoveSQL题目来源:buuctf题目类型:web涉及考点:SQL注入1.题目页面给了两个输入框与之前相同,我们先随便输入数进去,获得回显页面:我们直接使用万能密码登录:只需用户名输入1'or1=1#即可原因是:当用户名输入1,密码输入1'时,发生报错当用户名输入1'#,密码输入1......
  • pstack
    pstack显示每个进程的栈跟踪补充说明pstack命令可显示每个进程的栈跟踪。pstack命令必须由相应进程的属主或root运行。可以使用pstack来确定进程挂起的位置。此命令允许使用的唯一选项是要检查的进程的PID。命令软件包下载地址:https://packages.debian.org/sid/pstack......
  • OpenStack多云管理
    OpenStack多云管理简介OpenStack是一个开源的云计算平台,包含了一系列的组件和工具,可以用于构建和管理私有云、公有云以及混合云等多云环境。其中,多云管理是OpenStack的重要功能之一,它提供了灵活的部署和管理选项,使用户能够轻松地在不同的云环境中进行资源的调度和迁移。多云管理......
  • OpenStack安装失败
    OpenStack安装失败的解决方法作为一名经验丰富的开发者,我很高兴能够帮助到你解决OpenStack安装失败的问题。在开始解决问题之前,让我们先了解一下整个安装过程的流程,并逐步介绍每个步骤需要做什么以及所需的代码。安装流程根据我的经验,OpenStack的安装一般分为以下几个步骤:步......
  • OpenStack 网络 不通 根据
    OpenStack网络不通根据介绍OpenStack是一个开源的云计算平台,它提供了一套完整的解决方案来构建和管理私有云和公有云环境。在OpenStack中,网络是一个重要的组件,它允许虚拟机之间进行通信,并提供了对外部网络的连接。然而,有时候我们可能会遇到网络不通的问题,这篇文章将带你了解一些......
  • 使用MASA Stack+.Net 从零开始搭建IoT平台 第四章 4.4 查询历史数据
    @目录前言分析方案编写代码定义数据类编写查询方法添加ECharts图表效果总结前言IoT平台需要监控设备的运行状态,统计和分析设备传感器数据,使用图表展示是比较常见的场景。使用图表和表格数据组合的Dashboard也可以放在首页作为大屏展示。分析因为我们设备上报的数据都是存储......
  • openstack命令查看网络详情
    OpenStack命令查看网络详情作为一名经验丰富的开发者,我将向你介绍如何使用OpenStack命令查看网络详情。首先,让我们来整理一下整个流程。流程下面是使用OpenStack命令查看网络详情的步骤,我们将使用命令行界面:步骤描述步骤1登录到OpenStack环境步骤2列出可用的网络......
  • openstack可以做资源隔离吗
    OpenStack资源隔离实现流程OpenStack是一个开源的云计算平台,允许用户创建和管理虚拟机、网络和存储等资源。资源隔离是OpenStack的一个重要功能,通过隔离不同用户或项目的资源,可以确保安全性和性能的可控性。下面将介绍实现OpenStack资源隔离的步骤,并提供相应的代码示例。实现步骤......
  • OpenStack原理及在华为云中的应用
    1、云与操作系统虚拟化与云计算的区别虚拟化是将物理资源分配给多个虚拟机,提高硬件资源利用率,重点在于分配物理资源的能力云计算通过管理众多云虚拟机对外提供服务,重点在于提供服务。并且能够多租户之间隔离,按需使用、按量计费操作系统功能云也被当成操作系统,因为它也提供了:......
  • Openstack云计算简介
    一、什么是云计算云计算是一种计算模型,它将诸如运算能力、存储、网络和软件等资源抽象成为服务,以便让用户通过互联网远程享用,付费的形式也如同传统公共服务设施一样。因需而定、提供方便、动态改变和无限的虚拟化扩展能力是云计算的几个重要特征。不同的“云”对应着不同的基础设......