第27次CCF CSP(202209) T4非常详细题解 吉祥物投票(C++)

原题链接

前言:

        几个月前去的这场,只打了240分,t3做乱了,转到t4看完题目直接就开始写pushup和pushdown了,把线段树板子敲完才发现线段树没法做。。。最后连暴力都没写出来。老早就看了t4的官方题解,今天才想起来补,然后这题操作1属实有点难写,又因为两个bug调了两个小时。过两天就去下一场了,希望能>300分。

思路:

        我是看官方题解做出来的,所以思路和题解一样。

        操作1:总体是要维护1~n这些人的投票情况,最大1e9的n用数组维护无论从时间还是空间来看都不可能。所以考虑把连续的投票意愿相同的人看作一个块,用结构体表示,放到set里维护。

struct seg{
    int id;
    int l,r;
    bool operator < (const seg b)const{
        return l<b.l;
    }
};

        例如共有100个人,6~66号的人投票给1号作品,其余人还没意愿,那么我们只要维护 {{0,1,5},{1,6,66},{0,67,100}}这么一个有3个元素的集合就行,0表示无意愿,1表示意愿作品的id为1,这个集合就是提到的set。

        首先先说操作2、3:操作2、3如果要按题面意思模拟的话不出意外会超时。所以要采取的办法就是映射,假如要把3所有票数给6,就把3映射到6,这样在找到3的时候,就知道其实它是6的一部分。为了便于理解实现这一操作,我们有两个定义,一个叫编号,一个叫id(我乱起的)。编号就是那1~m,x号吉祥物的编号就是x,一个吉祥物自始至终只有一个编号;id是从属于编号的一个标识符号,一个吉祥物可以有很多个id,这些id都表示这一个吉祥物。而我们上面提到的结构题里存的是id,而不是存编号,我们可以通过id找编号,所以只对id做变换就可以。刚开始,x号吉祥物有一个编号x和一个id:x。要实现操作2,即把x的票数给y,就只需要把x的所有id给到y。要实现操作3,就只需要交换x和y全部的id。这种集合的合并直接用并查集,具体实现是用一个h数组,h[x]连接着一个并查集的祖宗节点,这个祖宗节点也通过一个pre数组连向x,这个并查集里的节点全是x的id,就是这个图的样子,操作时2合并并查集,3交换并查集就可以了。

        举个例子,某一时刻6有id:3和6,9有id:9。把6和9的票数交换,就只需要交换两个的id,于是6就有id:9,9有id:3和6,而我们的set集合不需要任何变换。在以后操作1更新集合时,比如我们遍历到了id为3或6的块,我们就能找到这其实是属于编号为9的作品的块,从而对9号作品进行该有的更新。差不多就是这样。

        

        操作4、5:用一个tot数组维护所有作品的得票数,0表示无意愿的人数。但这里的下标是作品的编号,没有任何映射,6就是6号作品的票数,100就是100。而得票最大值就再开个set来模拟一下heap维护最大值就行了。然后操作1、2、3都需要更新tot的值,再用tot的值更新heap里的值。

        每次操作1呢,就对set用upperbound求出来和[l,r]相交的两段,这两段之间的所有块都要处理,都要把这些块的id对应的编号找出来,然后更新tot的值,和[l,r]相交的两端要分割(删除再插入),中间的直接删了,挺难写的,细心点处理就行了。

#include<bits/stdc++.h>
using namespace std;

struct seg{
    int id;
    int l,r;
    bool operator < (const seg b)const{
        return l<b.l;
    }
};
struct Node{
    int id,cnt;
    bool operator > (const Node b)const{
        if(cnt==b.cnt) return id<b.id; 
        return cnt>b.cnt;
    }
};
int n,m,q,idx,op;
int tot[100010];
int p[200010],h[200010],pre[200010];
set<seg> se;
set<Node,greater<Node> > heap;

void init(){//初始化
    tot[0]=n;
    se.insert({0,1,n});
    for(int i=0;i<=m;i++){
        p[i]=h[i]=pre[i]=i;
        heap.insert({i,tot[i]});
    }
    idx=m+1;
}

int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}

int mid(int x){//通过编号找到任一个id
    return h[x];
}

int idm(int x){//通过id找到唯一的编号
    return pre[find(x)];
}

void modify(int x,int l,int r){//操作1
    auto ita=se.upper_bound({0,l,0});
    auto itb=se.upper_bound({0,r,0});
    ita--;
    // cout<<(*ita).l<<(*ita).r<<endl;
    vector<seg> win;//待新插入的节点
    vector<set<seg>::iterator> wer;//待删除的节点
    win.push_back({mid(x),l,r});
    auto temp=heap.find({x,tot[x]});
    heap.erase(temp);
    tot[x]+=(r-l+1);
    heap.insert({x,tot[x]});
    for(auto it=ita;it!=itb;it++) wer.push_back(it);
    for(int i=0;i<wer.size();i++){
        seg t=*wer[i];
        se.erase(t);
        if(l>=t.l&&r<=t.r){//[t.l  [l  r]    t.r]的情况
            if(l-1>=t.l) win.push_back({t.id,t.l,l-1});
            if(r+1<=t.r) win.push_back({t.id,r+1,t.r});
            Node tc=*heap.find({idm(t.id),tot[idm(t.id)]});
            heap.erase(tc);
            tot[tc.id]-=(r-l+1);
            heap.insert({tc.id,tot[tc.id]});
        }
        else if(l<=t.r&&t.l<=l){//[t.l  [l   t.r]   r]的情况
            if(l-1>=t.l) win.push_back({t.id,t.l,l-1});
            Node tc=*heap.find({idm(t.id),tot[idm(t.id)]});
            heap.erase(tc);
            tot[tc.id]-=(t.r-l+1);
            heap.insert({tc.id,tot[tc.id]});
        }
        else if(r<=t.r&&t.l<=r){//[l    [t.l   r]  t.r]的情况
            if(r+1<=t.r) win.push_back({t.id,r+1,t.r});
            Node tc=*heap.find({idm(t.id),tot[idm(t.id)]});
            heap.erase(tc);
            tot[tc.id]-=(r-t.l+1);
            heap.insert({tc.id,tot[tc.id]});
        }
        else if(l<t.l&&r>t.r){//[l  [t.l  t.r]    r]的情况
            Node tc=*heap.find({idm(t.id),tot[idm(t.id)]});
            heap.erase(tc);
            tot[tc.id]-=(t.r-t.l+1);
            heap.insert({tc.id,tot[tc.id]});
        }
    }
    for(int i=0;i<win.size();i++) se.insert(win[i]);
}

void exchange(int x,int y){//操作3
    heap.erase({x,tot[x]}),heap.erase({y,tot[y]});
    int a=h[x],b=h[y];
    pre[a]=y,pre[b]=x;
    swap(h[x],h[y]);
    swap(tot[x],tot[y]);
    heap.insert({x,tot[x]});
    heap.insert({y,tot[y]});
}

void xtoy(int x,int y){//操作2
    heap.erase({x,tot[x]}),heap.erase({y,tot[y]});
    p[h[x]]=h[y];
    p[idx]=idx;
    h[x]=idx;
    pre[idx++]=x;
    tot[y]+=tot[x];
    tot[x]=0;
    heap.insert({x,tot[x]});
    heap.insert({y,tot[y]});
}

void debug(){//没用不用管
    cout<<"--------------------------------------"<<endl;
    for(auto it=se.begin();it!=se.end();it++){
        seg t=*it;
        cout<<t.l<<" "<<t.r<<" "<<idm(t.id)<<"          "<<tot[idm(t.id)]<<endl;
    }
    cout<<"--------------------------------------"<<endl;

    // cout<<"--------------------------------------"<<endl;
    // for(auto it=heap.begin();it!=heap.end();it++){
    //     Node t=*it;
    //     cout<<t.cnt<<" "<<tot[t.id]<<" "<<t.id<<endl;
    // }
    // cout<<"--------------------------------------"<<endl;
}

int main(){
    cin>>n>>m>>q;
    init();
    while(q--){
        // debug();
        cin>>op;
        if(op==1){
            int l,r,x;
            cin>>l>>r>>x;
            modify(x,l,r);
        }
        else if(op==2){
            int x,y;
            cin>>x>>y;
            xtoy(x,y);
        }
        else if(op==3){
            int x,y;
            cin>>x>>y;
            exchange(x,y);
        }
        else if(op==4){
            int x;
            cin>>x;
            cout<<tot[x]<<endl;
        }
        // else if(op==9){
        //     int x;
        //     cin>>x;
        //     cout<<"--->"<<idm(x)<<endl;
        // }
        else{
            if(tot[0]==n) cout<<0<<endl;
            else{
                if((*heap.begin()).id) cout<<(*heap.begin()).id<<endl;
                else cout<<(*(++heap.begin())).id<<endl;
            }
        }
    }
    system("pause");
}

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值