Codeforces 45C. Dancing Lessons (SBT)


题意:n个人站成一行,从左到右编号1到n,每人一个技巧值。若相邻的两个人性别不同,则这两人有可能一起跳舞。

每次操作,从所有可能一起跳舞的两人中,选出技巧最接近的两人,若有多种方案,则选择最靠左边的(编号最小的)。

然后其余的人合并在一起,不留空(编号不变)。继续操作,直到最后结束,问所有的出来跳舞的人的编号以及顺序。


首先,用 next 数组表示每个人的右边的第一个人的编号,-1表示没有。

用 last 数组表示每个人左边的第一个人的编号,-1表示没有。

用平衡树(SBT)按照差值来存放所有可能的配对情况,按照第一键值D(技巧差值),第二键值L(左边那人的编号)来排序(D 和 L 可以唯一表示一种配对)

每次从平衡树中拿出最小的一个(根据上面定义的小于,树中最小的就是要求的答案)

假设拿出的坐标为 L R,

那么,每拿出一对(L,R) ,就要删除(last [ L ] , L)  (L,R)  (R , next [ R ] )  然后加入 (last [ L ] , next [ R ] )  (当然这里的加入删除都是在性别不同的情况下才操作)

然后更新 last [ L ] 的next 和  next [ R ] 的 last 。


其实优先队列也可以做,但是平衡树更熟练一点,于是就写了SBT,这里写了带空间回收的版本。

代码如下:


#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#define maxn 200007
#define NODE(i,j) Node(i,j,abs(A[i]-A[j]))
using namespace std;
//SBT 
queue<int> Lost;
int next[maxn],last[maxn];
struct Node{
	int L,R,D;
	Node(){}
	Node(int L,int R,int D):L(L),R(R),D(D){}
	bool operator <(const Node &B)const{return D < B.D||D==B.D&&L < B.L;}
	bool operator ==(const Node &B)const{return L==B.L&&R==B.R;}
}K[maxn];
int L[maxn],R[maxn],S[maxn],IP,T;
void zig(int &x){int t=R[x];R[x]=L[t];L[t]=x;S[t]=S[x];S[x]=S[L[x]]+S[R[x]]+1;x=t;}
void zag(int &x){int t=L[x];L[x]=R[t];R[t]=x;S[t]=S[x];S[x]=S[L[x]]+S[R[x]]+1;x=t;}
void lev(int &x,bool left){
	if(left){
		if(S[L[L[x]]]>S[R[x]]) zag(x);
		else if(S[R[L[x]]]>S[R[x]]) zig(L[x]),zag(x);
		else return;
	}
	else{
		if(S[R[R[x]]]>S[L[x]]) zig(x);
		else if(S[L[R[x]]]>S[L[x]]) zag(R[x]),zig(x);
		else return;
	}
	lev(L[x],true);lev(R[x],false);
	lev(x,true);lev(x,false);
}
void Insert(int &rt,Node X){
	if(!rt){
		if(!Lost.empty()) {rt=Lost.front();Lost.pop();}
		else rt=++IP;
		L[rt]=R[rt]=0;
		S[rt]=1;
		K[rt]=X;
		return;
	}
	X < K[rt]?Insert(L[rt],X):Insert(R[rt],X);
	++S[rt];lev(rt,X < K[rt]);
}
Node Delete(int &rt,Node X){
	Node Del;--S[rt];
	if(K[rt]==X || X < K[rt] && !L[rt] || K[rt] < X && !R[rt]){
		Del=K[rt];
		if(!L[rt]||!R[rt]){Lost.push(rt);rt=L[rt]+R[rt];}
		else K[rt]=Delete(L[rt],X);
	}
	else Del=X < K[rt]?Delete(L[rt],X):Delete(R[rt],X);
	lev(rt,K[rt] < X);
	return Del;
}
int Min(int &rt){return L[rt]?Min(L[rt]):rt;}

//信息 
int n;
int A[maxn];
char str[maxn];
int X[maxn],Y[maxn],N;
int main(void)
{
	while(~scanf("%d",&n)){
		//读取输入 
		scanf("%s",str+1);
		for(int i=1;i<=n;++i) scanf("%d",&A[i]);
		//初始化SBT 
		while(!Lost.empty()) Lost.pop();
		L[0]=R[0]=S[0]=IP=T=N=0;
		//初始化next和last数组 
		for(int i=1;i<n;++i)  next[i]=i+1;next[n]=-1;
		for(int i=2;i<=n;++i) last[i]=i-1;last[1]=-1;
		//加入所有可能的配对 
		for(int i=1;i<n;++i){
			if(str[i]^str[i+1]) Insert(T,NODE(i,i+1));
		}
		//开始计算 
		while(S[T]){// last[L] L R next[R]
			//记录拿出的点 
			++N;
			int rt=Min(T);
			X[N]=K[rt].L;
			Y[N]=K[rt].R;
			//更新平衡树 
			Delete(T,K[rt]);//删除 (L,R) 
			
			if(~last[K[rt].L]) {//如果 L 前面还有 
				if(str[last[K[rt].L]]^str[K[rt].L]){
					Delete(T,NODE(last[K[rt].L],K[rt].L));//删除(last[L],L)
				}
				next[last[K[rt].L]]=next[K[rt].R];
			}
			
			if(~next[K[rt].R]){//如果 R 后面还有 
				if(str[K[rt].R]^str[next[K[rt].R]]){
					Delete(T,NODE(K[rt].R,next[K[rt].R]));//删除(R,next[R]) 
				}
				last[next[K[rt].R]]=last[K[rt].L];
			}
			
			if(~last[K[rt].L]&&~next[K[rt].R]){//如果 前后都有 
				if(str[last[K[rt].L]]^str[next[K[rt].R]]){
					Insert(T,NODE(last[K[rt].L],next[K[rt].R]));//加入(last[L],next[R]) 
				}
			}
		}
		//输出答案 
		printf("%d\n",N);
		for(int i=1;i<=N;++i){
			printf("%d %d\n",X[i],Y[i]);
		}
	}
return 0;
}




你可能感兴趣的:(codeforces)