洛谷题单 【数据结构2-1】二叉堆与树状数组

洛谷题单 【数据结构2-1】二叉堆与树状数组

P1878 舞蹈课 优先队列+模拟

题目链接

每次要取出最小值,考虑用优先队列维护。维护出跳舞的两个人的 id 以及技术相差值。

然后每次取出队头元素,判断左右两边的是否可以构成新的舞伴加入优先队列即可。

注意:每个人只能和一个人组成舞伴,所以要标记一下。以及对于 l,r 来说,他的左右两边不一定是

l1r+1 。所以我们再维护一下每个位置的前驱和后继即可(双向链表)。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

struct node{
    int l,r;
    int val;
    bool operator<(const node &u)const{
        return (val==u.val?l>u.l:val>u.val);
    }
};

void Showball(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    s="?"+s;
    vector<int> a(n+1),pre(n+2),suf(n+2);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        pre[i]=i-1;
        suf[i]=i+1;
    }
    priority_queue<node> pq;
    for(int i=1;i<n;i++){
        if(s[i]!=s[i+1]) pq.push({i,i+1,abs(a[i]-a[i+1])});
    }

    vector<array<int,2>> ans;
    vector<int> st(n+1);
    while(pq.size()){
        node u;
        while(1){
            u=pq.top();
            pq.pop();
            if(pq.empty()||!st[u.l]&&!st[u.r]) break;
        }
        if(st[u.l]==1||st[u.r]==1) continue;
        ans.push_back({u.l,u.r});
        st[u.l]=st[u.r]=1;
        int l=pre[u.l],r=suf[u.r];
        pre[r]=l;suf[l]=r;
        if(l>=1&&r<=n&&s[l]!=s[r]) pq.push({l,r,abs(a[l]-a[r])});
    }
    cout<<ans.size()<<"\n";
    for(auto [l,r]:ans) cout<<l<<" "<<r<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

对顶堆

img

P1168 中位数 对顶堆

题目链接

用对顶堆维护好单调序列,并且维护两个堆数量均衡,那么最后元素多一个的堆顶就是中位数。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
     int n;
     cin>>n;
     priority_queue<int,vector<int>,greater<int>> minq;
     priority_queue<int> maxq;
     
     int s1=0,s2=0;
     for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        if(i==1) maxq.push(x),s1++;
        else{
            int v=maxq.top();
            if(x>=v) minq.push(x),s2++;
            else maxq.push(x),s1++;
        }
        if(s1-s2==2){
            minq.push(maxq.top());
            maxq.pop();
            s1--;s2++;
        }
        if(s2-s1==2){
            maxq.push(minq.top());
            minq.pop();
            s1++;s2--;
        }
        if(i&1){
            if(s1>s2) cout<<maxq.top()<<"\n";
            else cout<<minq.top()<<"\n";
        }
     }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

P2168 [NOI2015] 荷马史诗 堆+哈夫曼树

K 叉哈夫曼树构造题

模仿 合并果子 的方式,每 k 个一合并即可。最后,我们需要注意一个细节。因为每次都是将k个节点合并为1个(减少k-1个),一共要将n个节点合并为1个,如果(n-1)%(k-1)!=0 则最后一次合并时不足k个。也就表明了最靠近根节点的位置反而没有被排满,因此我们需要加入k-1-(n-1)%(k-1)个空节点使每次合并都够k个节点(也就是利用空节点将其余的节点挤到更优的位置上)。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

struct node{
    i64 w,h;
    bool operator<(const node &u)const{
        return (w==u.w?h>u.h:w>u.w);
    }
};

void Showball(){
    int n,k;
    cin>>n>>k;
    priority_queue<node> pq;
    for(int i=1;i<=n;i++){
        i64 x;
        cin>>x;
        pq.push({x,1});
    }     
    int cnt=(k-1-(n-1)%(k-1))%(k-1);
    for(int i=0;i<cnt;i++) pq.push({0,1});
    cnt+=n;
    i64 ans=0;
    while(cnt>1){
        i64 tmp=0,maxh=0;
        for(int i=0;i<k;i++){
            tmp+=pq.top().w;
            maxh=max(maxh,pq.top().h);
            pq.pop();
        }
        ans+=tmp;
        pq.push({tmp,maxh+1});
        cnt-=k-1;
    }
    cout<<ans<<"\n"<<pq.top().h-1;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

P2161 [SHOI2009] 会场预约 set/线段树染色

题目链接

set

可以用之前的经典Trick,通过重载运算符,利用 setfind 函数快速找到相交区间。

然后暴力统计即可。代码十分好写。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

struct node{
    int l,r;
    bool operator<(const node &u)const{
        return r<u.l;
    }
};
void Showball(){
    int n;
    cin>>n;
    set<node> st;
    while(n--){
        char op;
        cin>>op;
        if(op=='A'){
            int l,r;
            cin>>l>>r;
            auto it=st.find({l,r});
            int cnt=0;
            while(it!=st.end()){
                cnt++;
                st.erase(it);
                it=st.find({l,r});
            }
            st.insert({l,r});
            cout<<cnt<<"\n";       
        }else{
            cout<<st.size()<<"\n";
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

线段树染色

对于 A 操作,首先查询区间颜色数量,然后将区间重新染色即可。

对于 B 操作,直接查询整个区间颜色数量即可。

懒标记:0表示未染色,-1表示区间多种颜色,其余值表示颜色值。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

const int N=2e5+10;

int ans,cnt;
int st[N];
//线段树 染色
struct node{
    int l,r;
    int color;
    int tag;//0 无颜色,-1 多种颜色
}tr[N*4];
#define ls u<<1
#define rs u<<1|1
void pushup(node &u,node &l,node &r){
    if(l.color==r.color) u.color=l.color;
    else u.color=-1;
}

void pushup(int u){
    pushup(tr[u],tr[ls],tr[rs]);
}

void addtag(node &u,int tag){
    if(!tag) return;
    u.color=tag;
    u.tag=tag;
}

void pushdown(int u){
    addtag(tr[ls],tr[u].tag);
    addtag(tr[rs],tr[u].tag);
    tr[u].tag=0;
}

void build(int u,int l,int r){
    tr[u].l=l,tr[u].r=r;
    tr[u].tag=0;tr[u].color=0;
    if(l==r) return;
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
    pushup(u);
}

void modify(int u,int l,int r,int tag){//区间修改
    if(l<=tr[u].l&&tr[u].r<=r) {
        addtag(tr[u],tag);
        return;
    }
    int mid=tr[u].l+tr[u].r>>1;
    pushdown(u);
    if(l<=mid) modify(ls,l,r,tag);
    if(r>mid) modify(rs,l,r,tag);
    pushup(u);
}

void query(int u,int l,int r){//区间查询
    if(l<=tr[u].l&&tr[u].r<=r){
        if(!tr[u].color) return;
        if(tr[u].color<0){
            pushdown(u);
            int mid=tr[u].l+tr[u].r>>1;
            if(l<=mid) query(ls,l,r);
            if(r>mid) query(rs,l,r);
        }else{
            if(!st[tr[u].color]){
                ans--;
                st[tr[u].color]=1;
                cnt++;
            }
        }
        return;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) query(ls,l,r);
    if(r>mid) query(rs,l,r);
}

void Showball(){
    int n;
    cin>>n;
    build(1,1,200000);
    for(int i=1;i<=n;i++){
        char op;
        cin>>op;
        if(op=='A'){
            int l,r;
            cin>>l>>r;
            query(1,l,r);
            cout<<cnt<<"\n";
            cnt=0;
            modify(1,l,r,i);
            ans++;
        }else{
            cout<<ans<<"\n";
        }
    } 
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}
posted @   Showball  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示