UOJ#218. 【UNR #1】火车管理 线段树 主席树

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html

题解

如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖、区间求和的线段树上完成这个问题。

于是本题的重点转到了如何求新的栈顶。

考虑用一个主席树维护一下每一个时刻每一个位置的栈顶元素的进栈时间,那么新的栈顶就是 当前位置栈顶的进栈时间-1 这时候的栈顶元素,然后这个东西也可以用我们维护的进栈时间来得到,所以我们只需要弄一个支持区间覆盖单点查询历史版本的主席树;这里区间覆盖有一个小技巧:假设节点 x 所代表的区间都被覆盖了,那么修改完 val[x] 之后令 ls[x] = rs[x] = x 即可。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
using namespace std;
typedef long long LL;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch==‘-‘,ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=500005;
int n,m,k;
int lastans=0;
int hisv[N];
namespace seg{
	const int S=N<<2;
	int sum[S],add[S],len[S];
	void build(int rt,int L,int R){
		len[rt]=R-L+1,sum[rt]=add[rt]=0;
		if (L==R)
			return;
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		build(ls,L,mid);
		build(rs,mid+1,R);
	}
	void pushdown(int rt){
		int ls=rt<<1,rs=ls|1;
		if (add[rt]){
			add[ls]=add[rs]=add[rt];
			sum[ls]=add[rt]*len[ls];
			sum[rs]=add[rt]*len[rs];
			add[rt]=0;
		}
	}
	void update(int rt,int L,int R,int xL,int xR,int v){
		if (R<xL||L>xR)
			return;
		if (xL<=L&&R<=xR){
			sum[rt]=v*len[rt];
			add[rt]=v;
			return;
		}
		pushdown(rt);
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		update(ls,L,mid,xL,xR,v);
		update(rs,mid+1,R,xL,xR,v);
		sum[rt]=sum[ls]+sum[rs];
	}
	int Query(int rt,int L,int R,int xL,int xR){
		if (R<xL||L>xR)
			return 0;
		if (xL<=L&&R<=xR)
			return sum[rt];
		pushdown(rt);
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		return Query(ls,L,mid,xL,xR)+Query(rs,mid+1,R,xL,xR);
	}
}
namespace pt{
	const int S=N*100;
	int val[S],ls[S],rs[S],root[N],cnt;
	void init(){
		root[0]=cnt=ls[1]=rs[1]=1;
		val[1]=0;
	}
	void update(int prt,int &rt,int L,int R,int xL,int xR,int v){
		if (R<xL||L>xR)
			return;
		if (rt==prt)
			rt=++cnt,val[rt]=val[prt],ls[rt]=ls[prt],rs[rt]=rs[prt];
		if (xL<=L&&R<=xR){
			val[rt]=v;
			ls[rt]=rs[rt]=rt;
			return;
		}
		int mid=(L+R)>>1;
		update(ls[prt],ls[rt],L,mid,xL,xR,v);
		update(rs[prt],rs[rt],mid+1,R,xL,xR,v);
	}
	int Query(int rt,int L,int R,int x){
		if (L==R)
			return val[rt];
		int mid=(L+R)>>1;
		if (x<=mid)
			return Query(ls[rt],L,mid,x);
		else
			return Query(rs[rt],mid+1,R,x);
	}
}
using pt::root;
int main(){
	n=read(),m=read(),k=read();
	seg::build(1,1,n);
	pt::init();
	hisv[0]=0;
	for (int T=1;T<=m;T++){
		root[T]=root[T-1];
		int type=read(),L=(read()+lastans*k)%n+1,R,x;
		if (type==1){
			R=(read()+lastans*k)%n+1;
			if (L>R)
				swap(L,R);
			printf("%d\n",lastans=seg::Query(1,1,n,L,R));
		}
		else if (type==2){
			int t1=pt::Query(root[T-1],1,n,L);
			t1=pt::Query(root[max(t1-1,0)],1,n,L);
			seg::update(1,1,n,L,L,hisv[t1]);
			pt::update(root[T-1],root[T],1,n,L,L,t1);
		}
		else if (type==3){
			R=(read()+lastans*k)%n+1,x=read();
			if (L>R)
				swap(L,R);
			hisv[T]=x;
			seg::update(1,1,n,L,R,x);
			pt::update(root[T-1],root[T],1,n,L,R,T);
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ218.html

时间: 2024-08-28 16:40:47

UOJ#218. 【UNR #1】火车管理 线段树 主席树的相关文章

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

可持久化线段树--主席树

浅谈可持久化线段树--主席树 权值线段树 权值线段树和普通线段树不一样的地方就是在于 它的结点存储的是区间内数的个数 这个线段树的好处就在于我们可以根据 左子树 和 右子树 的大小从而进行 查找某个数的排名 或者 查找排名为rk的数 可持久化的含义 可持久数据结构主要指的是我们可以查询历史版本的情况并支持插入,利用使用之前历史版本的数据结构来减少对空间的消耗(能够对历史进行修改的是函数式). 主席树的建树过程: 最开始的时候就是一个空树 然后我们再插入一个元素3 再加入一个元素 1 模版一 :求

小结:线段树 &amp; 主席树

概要: 就是用来维护区间信息,然后各种秀智商游戏. 应用: 优化dp.主席树等. 技巧及注意: size值的活用:主席树就是这样来的.支持区间加减,例题和模板:主席树,[BZOJ]1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树),[BZOJ]1901: Zju2112 Dynamic Rankings(区间第k小+树状数组套可持久化线段树(主席树)) 01(就是更新和不更新等这种对立操作)情况:我们就要在各个更新的操作中明白

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

4771: 七彩树 主席树

题意:给定一棵树  每个结点有一个颜色 然后又m个询问 询问:x d   问x的子树内不超过dep[x]+d 深度的子树结点一共有多少个颜色? 1.可以先将问题简化为问整个子树内有多少个不同的颜色  暴力解法树套树  但是可以用一个技巧来快速维护: 一个颜色一个颜色地处理  把所有相同颜色的点按照dfs序排序,每个点给自己的位置贡献1,相邻的两个点给lca贡献−1.然后只要区间内存在这种颜色,则其子树内的权值和必定为1.那么只需要这样子染好所有颜色之后询问子树和. 所以如果问题是这样的话只要一个

【BZOJ3439】Kpm的MC密码 trie树+主席树

Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每 一个k

bzoj 3545&amp;&amp;3551: [ONTAK2010]Peaks &amp;&amp;加强版 平衡树&amp;&amp;并查集合并树&amp;&amp;主席树

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

[BZOJ4539][HNOI2016]树(主席树)

4539: [Hnoi2016]树 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 746  Solved: 292[Submit][Status][Discuss] Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过这棵模板树来构建一颗大树.构建过程如下:(1)将模板树复制为初始的大树.(2)以下(2

权值线段与主席树总结

权值线段树就是把线段树的每个点权,赋予一定的含义,比如数字出现的次数,数值前缀出现的次数,并用区间求和维护一个区间信息,比如数字出现的次数,第K大等(不能实现区间第K大),前缀第K大等. 权值线段树优点: 能够比较容易实现平衡树的一系列操作 一个序列中,插入一个数,删除一个数,求值为数的排名,查询第K小的数,求比这个数小的数,求比这个数大的数. 上述操作都可以通过权值线段树实现.但是需要注意的是,上述操作数的范围如果过大,那么权值线段树将开不下,因为权值线段树存储的是节点的单点信息,也就是从1-