题意: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;
}