【题解】洛谷3285方伯伯的OJ (SCOI2014)

不想写 S p l a y Splay Splay 的请看这里!

首先,先对楼上那位用线段树解决此题的dalao l_h_j致敬——因为这个思路我们是同时想到的,但他只用了一次就搞定了,我却写了足足两个晚上(最后还因为把 20 20 20 写成 27 27 27 调了一个小时)。。。

接下来就是题解了:(华丽的分割线)


在看这个做法之前,请各位神犇先略微参考一下2017年 N O I P NOIP NOIP d a y 2 T 3 day2T3 day2T3 列队。这道题的线段树做法涉及到两个操作:从一个序列中删除一个数 以及 给这个序列末尾添加一个数。这个操作拓展一下就变成了一个支持两边添加的线段树,就可以轻松解决2,3两个询问了。

这个其实非常好解决,只需要在线段树的左右两边各预留出一个长度为 1 e 5 1e5 1e5 的空间,每次插入暴力把序列首位之前的一位或者序列末尾后面的一位+1即可。

询问1也没什么问题,只要用一个 m a p map map (用id[i]表示) 存一下每一个编号被改成了什么以及每一个当前编号的原始编号即可。这样每一次询问就和编号修改基本无关了。

那么我们现在就剩下两个问题了:查询某人的排名 以及 查询某个排名的人。

查询某人的排名很简单,你只要用 m a p map map 记录一下每一个编号的人在线段树里的位置,然后直接用线段树查询区间和即可。

但是查询某个排名的人怎么办呢?这个其实多想想也能得出做法:

因为线段树上每一个位置的值为非 0 及 1,所以我们直接二分这个人在线段树中的位置,然后判断当区间和为 x x x 时,这个人所在的位置是否合法即可。我们只需要用一个 m a p map map(其实就是反着记录一下 i d [ i ] id[i] id[i]) 记录一下线段树上每一个位置上的值的编号,然后直接输出就行了。

最后一定要记住: n n n 的范围是 1 e 8 1e8 1e8!二分得开到 2 27 2^{27} 227!(我就是在这里挂了一个小时 QwQ)

剩下不多说了,附上代码吧:

#include
#include
#include
#define lim 100000
#define N 20000005
#define M 100005
using namespace std;
int n,m,ans,cnt=1,sum[N],lazy[N],ls[N],rs[N];
map<int,int> id,lasid,pos,num,vispos,visnum,visid,vislasid;
void update(int i) {sum[i]=sum[ls[i]]+sum[rs[i]];}
void pushdown(int i,int l,int r)
{
	if(!lazy[i])	return;
	if(r-l<=0)	{lazy[i]=0; return;}
	int mid=(l+r)/2,v=lazy[i];
	if(!ls[i])	ls[i]=++cnt;
	if(!rs[i])	rs[i]=++cnt;
	sum[ls[i]]+=(mid-l+1)*v,sum[rs[i]]+=(r-mid)*v;
	lazy[ls[i]]+=v,lazy[rs[i]]+=v;
	lazy[i]=0;
}
void ins(int i,int l,int r,int L,int R,int v)
{
	if(!i)	return;
	pushdown(i,l,r);
	if(L<=l && r<=R) {sum[i]+=(r-l+1)*v,lazy[i]=v; return;}
	int mid=(l+r)/2;
	if(!ls[i])	ls[i]=++cnt;
	if(!rs[i])	rs[i]=++cnt;
	if(L<=mid)	ins(ls[i],l,mid,L,R,v);
	if(mid+1<=R)	ins(rs[i],mid+1,r,L,R,v);
	update(i);
}
int ques(int i,int l,int r,int L,int R)
{
	if(!i)	return 0;
	pushdown(i,l,r);
	if(L<=l && r<=R) {return sum[i];}
	int mid=(l+r)/2,ans=0;
	if(L<=mid)	ans+=ques(ls[i],l,mid,L,R);
	if(mid+1<=R)	ans+=ques(rs[i],mid+1,r,L,R);
	return ans;
}
int posi(int i) {return pos[i]?pos[i]:pos[i]=i;}
int numi(int i) {return num[i]?num[i]:num[i]=i;}
int idi(int i) {return id[i]?id[i]:id[i]=i-lim;}
int lasidi(int i) {return lasid[i]?lasid[i]:lasid[i]=i+lim;}
int main()
{
	int x,y,k;
	scanf("%d%d",&n,&m);
	int head=lim,tail=lim+n+1;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&k,&x);
		if(k==1)	scanf("%d",&y);
		x-=ans,y-=ans;
		int now=posi(lasidi(x));
		if(k==1)
		{
			id[lasidi(x)]=y,lasid[y]=lasidi(x);
			ans=now-head-ques(1,1,n+lim*2,head+1,now);
		}
		else if(k==2)
		{
			ans=now-head-ques(1,1,n+lim*2,head+1,now);
			ins(1,1,n+lim*2,now,now,1);
			pos[lasidi(x)]=head--,num[head+1]=lasidi(x);
		}
		else if(k==3)
		{
			ans=now-head-ques(1,1,n+lim*2,head+1,now);
			ins(1,1,n+lim*2,now,now,1);
			pos[lasidi(x)]=tail++,num[tail-1]=lasidi(x);
		}
		else
		{
			int las=head;
			for(int j=27;j>=0;j--)
				if(las+(1<<j)<tail && (las+(1<<j)-head-ques(1,1,n+lim*2,head+1,las+(1<<j)))<x)
					las+=(1<<j);
			ans=idi(numi(las+1));
		}
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(题解)