题意:
有 n个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。如果不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上(即:若队伍为 ABCD,那么 BC 出列之后队伍变为 AD)。舞蹈技术相差最小即是 ai的绝对值最小。
任务是模拟以上过程,确定跳舞的配对及顺序。
题解:
小顶堆的解法:
同样直接维护小顶堆,堆中值就是相邻两个异性的值之差。
那么首先建堆很容易,直接按照插入一个数的做法将相邻为异性的差塞到堆中,进行up操作即可。就算是GBGBGB这种也无所谓,我们只需要在后面判断某个人是否已经配对了即可。
那么现在只需要考虑删除以及后续的操作即可。这个时候上述情况就需要去考虑了,那么我们将已经取出来的人打个标记:lef[x]表示下标≤x的最后一个未被配对的人,rig[x]表示下标≥x的最早一个未被配对的人,这样我们就能在删掉L,R两个下标的人之后,快速地去找到下两个能匹配的人。
我在这里使用了路径压缩,也就是findl,findr,以防极端情况TLE。
这样删除h[1]节点之后,就将旁边的人加入进来,但是要注意到了边界(0和n+1)的情况。
同时,要注意由于之前塞到堆里面的人有可能被占用掉了,所以要判一下h[1]是否有人被占用即可,如果有就当做删除处理。
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=2e5+5;
int siz;//小顶堆
struct heap{
int val,l,r;
bool operator< (const heap&a)const {
if(val==a.val)return l<a.l;
return val<a.val;
}
}h[N];
void up(int pos){
while(pos!=1 && h[pos]<h[pos>>1])
swap(h[pos],h[pos>>1]),pos>>=1;
}
void down(int pos){
while((pos<<1)<=siz){
int ch=pos<<1;
if(ch+1<=siz && h[ch+1]<h[ch])
ch+=1;
if(h[ch]<h[pos])
swap(h[ch],h[pos]);
else break;
pos=ch;
}
}
int lef[N],rig[N],a[N];
int findl(int x){return x==lef[x]?x:lef[x]=findl(lef[x]);}
int findr(int x){return x==rig[x]?x:rig[x]=findr(rig[x]);}
char s[N];
vector<pii>ans;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n+1;i++)lef[i]=rig[i]=i;
scanf("%s",s+1);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<=n;i++)
if(s[i]!=s[i-1])
h[++siz]={abs(a[i]-a[i-1]),i-1,i},up(siz);
while(siz){
int l=h[1].l,r=h[1].r;
while(siz && (l!=lef[l] || r!=lef[r])){
swap(h[1],h[siz--]);
down(1);
l=h[1].l,r=h[1].r;
}
if(!siz)break;
ans.push_back({l,r});
lef[l]=lef[r]=findl(l-1),rig[r]=rig[l]=findr(r+1);
if(lef[l]!=0&&rig[r]!=n+1&&s[lef[l]]!=s[rig[r]])
h[++siz]={abs(a[lef[l]]-a[rig[r]]),lef[l],rig[r]};
}
printf("%d\n",ans.size());
for(pii i:ans)
printf("%d %d\n",i.first,i.second);
return 0;
}