Uoj #218. 【UNR #1】火车管理 可持久化线段树+思维

前几周考试的时候考了,正解给的是线段树套堆(专门卡这个做法的空间). 
 
这里先写一下可持久化线段树的做法(应该是官方正解吧). 
 
搞两个线段树,一个维护的是权值(查询答案时调用的).

另一个维护的是每个下标在每一个操作时刻被哪个操作在顶上覆盖着(这是一个可持久化线段树).

这样做有什么好处呢 ?

我们举个例子:
 
 假设这是一个 $l$ 位置的栈.

 当前要进行弹栈操作.

 在 $rt[i-1]$ 中查询,能查到 $o$ (最新一次压栈).

 我们知道,$o$ 压入的数是要被弹出去的,取而代之的是 $oo$ 所压入的数.
 
 我们在 $rt[o-1]$ 中再查询一次就可以查到 $oo$ 了.

 对应的,只需在第一颗维护权值的线段树中把对应点赋值为 $oo$ 所对应的权值即可.

 在可持久化线段树中的操作是新建一个 $i$ 版本,与 $i-1$ 唯一不同就是 $o->oo$.

 区间压栈在两颗线段树中对应的都是区间赋值. 
#include<bits/stdc++.h>
#define maxn 500005   
using namespace std;
int n,Q,ty,lastans=0; 
int rt[maxn], tim[maxn], lp[maxn], tp[maxn];     
void setIO(string s)
{
	string in=s+".in"; 
	string out=s+".out"; 
	freopen(in.c_str(),"r",stdin); 
	freopen(out.c_str(),"w",stdout);     
}
namespace tree1
{
	#define lson ( x<<1)
	#define rson ( (x<<1)|1)      
	int sumv[500005<<2],lazy[500005<<2];        
	void mark(int l,int r,int x,int k)
	{
		sumv[x]=(r-l+1)*k; 
		lazy[x]=k; 
	}
	void pushup(int x)
	{
		sumv[x]=sumv[lson]+sumv[rson]; 
	}
	void pushdown(int l,int r,int x)
	{
		if(!lazy[x]) return; 
		int mid=(l+r)>>1; 
		if(mid >= l) mark(l, mid, lson, lazy[x]); 
		if(r > mid) mark(mid + 1, r, rson, lazy[x]);     
		lazy[x]=0;     
	}
	void update(int l,int r,int x,int L,int R,int k)
	{
		if(l>=L&&r<=R)      
		{
			mark(l,r,x,k); 
			return; 
		}
		pushdown(l,r,x); 
		int mid=(l+r)>>1; 
		if(L<=mid) update(l,mid,lson,L,R,k); 
		if(R>mid) update(mid+1,r,rson,L,R,k); 
		pushup(x); 
	} 
	int query(int l,int r,int x,int L,int R)
	{
		if(l>=L&&r<=R) return sumv[x]; 
		pushdown(l, r, x); 
		int mid=(l+r)>>1, tmp=0; 
		if(L<=mid) tmp+=query(l,mid,lson,L,R); 
		if(R>mid) tmp+=query(mid+1,r,rson,L,R); 
		return tmp; 
	}
	#undef lson
	#undef rson 
}; 


namespace tree2
{
	int tot=0; 
	int tag[maxn*75],value[maxn*75];          
	int lson[maxn*75],rson[maxn*75];  
	void mark(int x,int delta)
	{
		value[x]=tag[x]=delta; 
		return; 
	}
	void pushdown(int l,int r,int x)
	{
		if(!tag[x]) return; 
		int mid=(l+r)>>1;         
		if(mid>=l && !lson[x]) lson[x]=++tot; 
		if(r > mid && !rson[x]) rson[x]=++tot;       
		if(lson[x]) mark(lson[x], tag[x]); 
		if(rson[x]) mark(rson[x], tag[x]); 
		tag[x]=0;  
	}        
	void add(int &k,int kk,int l,int r,int L,int R,int delta)
	{
		tag[k=++tot]=0;
		if(l>=L&&r<=R)       
		{
			mark(k, delta); 
			return; 
		}
		int mid=(l+r)>>1; 
		pushdown(l,r,kk); 
		lson[k]=lson[kk], rson[k]=rson[kk];            
		if(L <= mid) add(lson[k], lson[kk], l, mid, L, R, delta); 
		if(R > mid) add(rson[k], rson[kk], mid + 1, r, L, R, delta);      
	}
	int query(int l,int r,int k,int pos)
	{
		if(!k || l == r) return value[k]; 
		pushdown(l,r,k); 
		int mid=(l+r)>>1;
		if(pos<=mid) 
			return query(l,mid,lson[k], pos); 
		else 
			return query(mid+1,r,rson[k],pos); 
	}
}; 
int main()
{    
	// setIO("input"); 
	scanf("%d%d%d",&n,&Q,&ty); 
	int opt,l,r,dd; 	
	for(int i=1;i<=Q;++i) 
	{
		scanf("%d",&opt);    
		switch(opt)
		{
			case 1 : 
			{
				scanf("%d%d",&l,&r);    
				l=(l+lastans*ty)%n+1; 
				r=(r+lastans*ty)%n+1; 
				if(l>r) swap(l,r); 
				lastans=tree1::query(1,n,1,l,r);              
				printf("%d\n",lastans); 
				rt[i]=rt[i-1];          
				break; 
			}
			case 2 : 
			{ 
				scanf("%d",&l);     
				l=(l+lastans*ty)%n+1;  
				int o = tree2::query(1, n, rt[i-1],l);               //查询修改的 l 的修改操作.     
				if(!o) rt[i]=rt[i-1];                   
				else 
				{         
					int oo = tree2::query(1,n,rt[o-1],l);            // 上一次操作   
					tree1::update(1,n,1,l,l,tp[oo]);      //            
					tree2::add(rt[i], rt[i-1], 1, n, l,l,oo);         
				}
				break; 
			}
			case 3 : 
			{
				scanf("%d%d%d",&l,&r,&dd); 
				l=(l+lastans*ty)%n+1; 
				r=(r+lastans*ty)%n+1; 
				if(l>r) swap(l,r);  
				tp[i]=dd; 
				tree1::update(1,n,1,l,r,dd);      
				tree2::add(rt[i],rt[i-1],1,n,l,r,i);                
				break; 
			}
		}
	}
	return 0; 
}

  

posted @ 2019-06-14 12:38  EM-LGH  阅读(219)  评论(0编辑  收藏  举报