洛谷 P1878 舞蹈课 —— 小顶堆

This way

题意:

有 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;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值