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

Posted zhouzhendong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
",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;
}

  

以上是关于UOJ#218. UNR #1火车管理 线段树 主席树的主要内容,如果未能解决你的问题,请参考以下文章

uoj#218. UNR #1火车管理

UOJ UNR #1火车管理 可持久化线段树

[UOJ218]火车管理

uoj 218 火车管理

UOJ#217. UNR #1奇怪的线段树(广义线段树性质+上下界最小流)

uoj#388. UNR #3配对树(线段树合并)