P3960 列队

https://www.luogu.com.cn/problem/P3960

看有写平衡树的,不过其实发现由于这个题插入的点都是在边上,所以完全可以用一个动态开点的线段树来做

(n+1) 个线段树来维护,前 (n) 个分别维护 (n) 行的 (1)(m-1) 位置的点,第 (n+1) 棵维护第 (m)(n) 个位置的信息
有很多组点,它们始终是一起运动的,所以也就没必要把它们都拿出来单独分配内存,这也是动态开点的目的
每次当取出 ((x,y)) 时:

  • 如果 (y eq m),就去第 (x) 个线段树取出第 (y) 个点,删除它(如何实现删除?其实就是维护一个 (size),然后递归时用 (size) 判断进左儿子还是右儿子),再把他添加到第 (n+1) 个树的末尾(代表了那个离队的同学归队,同时由于动态开店的线段树也要在一开始就确定长度,所以每个树的大小应为 (max(n,m)+q)
    然后第 (x) 行同学往左补队的时候,((x,m)) 的位置,离开第 (n+1) 棵线段树,进入第 (x)
  • 如果 (y=m),就直接取出 (n+1) 棵线段树的第 (x) 个点,再放到最后。然后就没有往左补队的那一套了

发现上述过程并没有体现出某一个区间的位置整体移动的过程,也正是因此保证了复杂度
其实刚才说的删除点更像是惰性删除,只是减小 (size) 从而让这个点不会再被访问到,这个在线段树上删除点的思路在之前似乎还没见到过

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m;
int num[300006];
struct tr{
	tr *ls,*rs;
	long long w;
	int size;
}dizhi[30000006];
int tot;
struct segment{
	tr *root;
	inline int get(int l,int r,int now){
		if(now==n+1){
			if(r<=n) return r-l+1;
			if(l<=n) return n-l+1;
			return 0;
		}
		if(r<m) return r-l+1;
		if(l<m) return m-l;
		return 0;
	}
	long long ask(tr *&tree,int l,int r,int pos,int now){
		if(!tree){
			tree=&dizhi[++tot];
			tree->size=get(l,r,now);
			if(l==r){
				if(now==n+1) tree->w=(long long)l*m;
				else tree->w=(long long)(now-1)*m+l;
			}
		}
		tree->size--;
		if(l==r) return tree->w;
		int mid=(l+r)>>1;
		if((tree->ls&&pos<=tree->ls->size)||(!tree->ls&&pos<=mid-l+1))
			return ask(tree->ls,l,mid,pos,now);
		if(!tree->ls) pos-=mid-l+1;
		else pos-=tree->ls->size;
		return ask(tree->rs,mid+1,r,pos,now);
	}
	void change(tr *&tree,int l,int r,int pos,long long w,int now){
		if(!tree){
			tree=&dizhi[++tot];
			tree->size=get(l,r,now);
			if(l==r) tree->w=w;
		}
		tree->size++;
		if(l==r) return;
		int mid=(l+r)>>1;
		pos<=mid?change(tree->ls,l,mid,pos,w,now):change(tree->rs,mid+1,r,pos,w,now);
	}
}tree[300006];
int main(){
	n=read();m=read();int q=read();
	reg int x,y;long long tmp;
	int len=std::max(n,m)+q;
	for(reg int i=1;i<=n+1;i++) tree[i].root=&dizhi[++tot],tree[i].root->size=m;
	tree[n+1].root->size=n;//root 的 size 提前设置
	while(q--){
		x=read();y=read();
		if(y==m) printf("%lld
",tmp=tree[n+1].ask(tree[n+1].root,1,len,x,n+1));
		else printf("%lld
",tmp=tree[x].ask(tree[x].root,1,len,y,x));
		tree[n+1].change(tree[n+1].root,1,len,n+(++num[n+1]),tmp,n+1);
		if(y^m){
			tmp=tree[n+1].ask(tree[n+1].root,1,len,x,n+1);
//				printf("	tmp=%d
",tmp);
			tree[x].change(tree[x].root,1,len,m-1+(++num[x]),tmp,x);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/13768943.html