码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-02-22 21:42:47      阅读:190      评论:0      收藏:0      [点我收藏+]

标签:ace   html   type   lse   printf   ret   init   using   线段树   

原文链接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;
}

  

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

标签:ace   html   type   lse   printf   ret   init   using   线段树   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!