题面传送门

题意:

有 \(n\) 个关卡,第 \(i\) 个关卡玩到 \(1\) 颗星需要花 \(a_i\) 的时间,玩到 \(2\) 颗星需要 \(b_i\) 的时间。(\(a_i<b_i\))

求玩到 \(w\) 颗星最少需要多少时间。

\(1 \leq n \leq 3 \times 10^5\)

yyq:“好题不常有,做过了就不能再错过了”

首先有一个显然的错误解法:初始将所有 \(a_i\) 压入优先队列,然后每次弹出优先队列中最小的值,如果它是一星的话就将 \(b_i-a_i\) 压入优先队列。

hack:

2 2
3 4
2 5

下面介绍三种解法:

  1. 直接对着上面的贪心进行魔改

    上面的贪心错误的原因是因为没有权衡好选择两星还是选择一星。

    我们可以再建立一个优先队列保存当前零星的关卡中,\(b_i\) 的最小值。

    如果我们发现,当前零星的关卡中 \(b_i\) 的最小值 \(<\) 一星的最小的两个值之和。

    那么我们就把 \(i\) 关卡玩到一星,并将 \(b_i-a_i\) 压入一星的队列当中。

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    const int MAXN=3e5+5;
    int n,m,a[MAXN<<1],vis[MAXN<<1],star[MAXN];
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > o,t;
    void clear(priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > &q){
    while(!q.empty()&&vis[q.top().se]) q.pop();
    }
    int main(){
    scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&a[i+n]),a[i+n]-=a[i];
    for(int i=1;i<=n;i++) o.push(mp(a[i],i)),t.push(mp(a[i]+a[i+n],i));
    ll ans=0;
    while(m--){
    clear(o);clear(t);
    int i=o.top().se;o.pop();clear(o);
    if(m&&t.size()&&a[i]+o.top().fi>=t.top().fi)
    o.push(mp(a[i],i)),i=t.top().se,t.pop();
    if(i<=n) o.push(mp(a[i+n],i+n));
    ans+=a[i];star[(i>n)?(i-n):i]++;vis[i]=true;
    } printf("%lld\n",ans);
    for(int i=1;i<=n;i++) printf("%d",star[i]);
    return 0;
    }
  2. 反悔贪心

    考虑每次增加一颗星星,取最小的增加方式。

    按照最朴素的贪心有两种增加方式:

    I. 将一个零星的关卡 \(i\) 改为一星,代价为 \(a_i\)

    II. 将一个一星的关卡 \(i\) 改为两星,代价为 \(b_i-a_i\)

    我们利用返回贪心的思想,还有两种反悔方式:

    III. 将一个一星关卡 \(i\) 改为零星关卡,并将一个零星关卡 \(j\) 改为两星关卡,代价为 \(b_j-a_i\)

    IV. 将一个两星关卡 \(i\) 改为一星关卡,并将一个零星关卡 \(j\) 改为两星关卡,代价为 \(b_j-(b_i-a_i)\)

    考虑怎样维护这个最小值,我们建立五个小根堆:

    \(q_1:\min\{a_i|star_i=0\}\)

    \(q_2:\min\{b_i-a_i|star_i=1\}\)

    \(q_3:\min\{b_i|star_i=0\}\)

    \(q_4:\min\{-a_i|star_i=1\}\)

    \(q_5:\min\{-b_i+a_i|star_i=2\}\)

    对于方式 I,取出 \(q_1\) 的队首。

    对于方式 II,取出 \(q_2\) 的队首。

    对于方式 III,取出 \(q_3\) 和 \(q_4\) 的队首并将它们相加。

    对于方式 III,取出 \(q_3\) 和 \(q_5\) 的队首并将它们相加。

    还有个注意点:有些关卡星值的修改会牵扯到两个堆,我们要在每一次取状态时判断状态是否满足,不满足就 pop 掉。

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    const ll INF=1e18;
    const int MAXN=3e5+5;
    int n,m,a[MAXN],b[MAXN],star[MAXN];
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q1,q2,q3,q4,q5;
    //q1: min{a[i]} star[i]=0
    //q2: min{b[i]-a[i]} star[i]=1
    //q3: min{b[i]} star[i]=0
    //q4: min{-a[i]} star[i]=1
    //q5: min{-b[i]+a[i]} star[i]=2
    int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
    q1.push(mp(INF,n+1));q2.push(mp(INF,n+1));q3.push(mp(INF,n+1));
    q4.push(mp(INF,n+1));q5.push(mp(INF,n+1));star[n+1]=-1;
    for(int i=1;i<=n;i++) q1.push(mp(a[i],i)),q3.push(mp(b[i],i));
    ll ans=0;
    for(int i=1;i<=m;i++){
    while(q1.size()>1&&star[q1.top().se]!=0) q1.pop();
    while(q2.size()>1&&star[q2.top().se]!=1) q2.pop();
    while(q3.size()>1&&star[q3.top().se]!=0) q3.pop();
    while(q4.size()>1&&star[q4.top().se]!=1) q4.pop();
    while(q5.size()>1&&star[q5.top().se]!=2) q5.pop();
    ll x1=q1.top().fi,x2=q2.top().fi,x3=q3.top().fi+q4.top().fi,x4=q3.top().fi+q5.top().fi;
    ll mn=min(min(x1,x2),min(x3,x4));ans+=mn;
    // printf("%lld\n",mn);
    if(x1==mn){
    int j=q1.top().se;star[j]=1;q1.pop();
    q2.push(mp(b[j]-a[j],j));q4.push(mp(-a[j],j));
    } else if(x2==mn){
    int j=q2.top().se;star[j]=2;q2.pop();
    q5.push(mp(-b[j]+a[j],j));
    } else if(x3==mn){
    int j1=q3.top().se,j2=q4.top().se;
    star[j1]=2;star[j2]=0;q3.pop();q4.pop();
    q5.push(mp(-b[j1]+a[j1],j1));
    q1.push(mp(a[j2],j2));q3.push(mp(b[j2],j2));
    } else{
    int j1=q3.top().se,j2=q5.top().se;
    star[j1]=2;star[j2]=1;q3.pop();q5.pop();
    q5.push(mp(-b[j1]+a[j1],j1));
    q2.push(mp(b[j2]-a[j2],j2));q4.push(mp(-a[j2],j2));
    }
    }
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++) printf("%d",star[i]);
    return 0;
    }
  3. 数据结构

    我们将这 \(n\) 个关卡按 \(b_i\) 从小到大排序。

    假设我们选择的两星的关卡中 \(b_i\) 最大的为关卡 \(l\),那么在关卡 \(l\) 前面的所有关卡都至少选择了一颗星(注意我们已经按 \(b_i\) 从小到大排好序了)。

    为什么呢?很简单,如果有一个关卡 \(j\) 一颗星都没选,那么把 \(b_l\) 改为零星,\(b_j\) 改为两星肯定更优。

    又因为关卡 \(l\) 是选择的两星的关卡中 \(b_i\) 最大的一个,所以在 \(l\) 后面的关卡至多选了一星。

    维护 \(c_i=\begin{cases}b_i-a_i&(i\leq l)\\a_i&(i>l)\end{cases}\)

    然后取 \(c_i\) 最小的 \(w-l\) 个就行了。

    实现方式很多,我使用的是将 \(b_i-a_i\) 与 \(a_i\) 揉在一起离散化,然后线段树上二分。

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    bool chkmin(ll &x,ll y){return ((x>y)?(x=y,1):0);}
    bool chkmax(ll &x,ll y){return ((x<y)?(x=y,1):0);}
    const int MAXN=3e5+5;
    int n,w,a[MAXN],b[MAXN],key[MAXN<<1],hs[MAXN<<1],cnt=0,num=0,ord[MAXN];
    int get(int x){return lower_bound(hs+1,hs+num+1,x)-hs;}
    struct node{
    int l,r,c;ll v;
    } s[MAXN<<3];
    void build(int k,int l,int r){
    s[k].l=l;s[k].r=r;if(l==r) return;
    int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    }
    void modify(int k,int x,int addv,int addc){
    if(s[k].l==s[k].r){s[k].c+=addc;s[k].v+=addv;return;}
    int mid=(s[k].l+s[k].r)>>1;
    if(x<=mid) modify(k<<1,x,addv,addc);
    else modify(k<<1|1,x,addv,addc);
    s[k].c=s[k<<1].c+s[k<<1|1].c;
    s[k].v=s[k<<1].v+s[k<<1|1].v;
    }
    ll query(int k,int c){
    if(s[k].l==s[k].r) return 1ll*c*hs[s[k].l];
    if(c>=s[k<<1].c) return s[k<<1].v+query(k<<1|1,c-s[k<<1].c);
    else return query(k<<1,c);
    }
    bool cmp(int x,int y){return b[x]<b[y];}
    int star[MAXN];pii p[MAXN];
    int main(){
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
    for(int i=1;i<=n;i++) ord[i]=i;sort(ord+1,ord+n+1,cmp);
    // for(int i=1;i<=n;i++) printf("%d\n",ord[i]);
    for(int i=1;i<=n;i++) key[++cnt]=a[i],key[++cnt]=b[i]-a[i];
    sort(key+1,key+cnt+1);
    for(int i=1;i<=cnt;i++) if(key[i]!=key[i-1]) hs[++num]=key[i];
    build(1,1,num);
    for(int i=1;i<=n;i++) modify(1,get(a[i]),a[i],1);
    ll ans=1e18,sum=0;if(w<=n) ans=min(ans,query(1,w));
    int pos=0;
    for(int i=1;i<=n;i++){
    modify(1,get(a[ord[i]]),-a[ord[i]],-1);
    modify(1,get(b[ord[i]]-a[ord[i]]),b[ord[i]]-a[ord[i]],1);
    sum+=a[ord[i]];//printf("%lld\n",sum);
    // printf("%lld\n",query(1,w-i));
    if(w-i<=n&&w-i>=0) if(chkmin(ans,query(1,w-i)+sum)) pos=i;
    } printf("%lld\n",ans);
    for(int i=1;i<=pos;i++) star[ord[i]]++;
    for(int i=1;i<=pos;i++) p[i]=mp(b[ord[i]]-a[ord[i]],ord[i]);
    for(int i=pos+1;i<=n;i++) p[i]=mp(a[ord[i]],ord[i]);
    sort(p+1,p+n+1);for(int i=1;i<=w-pos;i++) star[p[i].se]++;
    for(int i=1;i<=n;i++) printf("%d",star[i]);
    return 0;
    }

Codeforces 436E - Cardboard Box(贪心/反悔贪心/数据结构)的更多相关文章

  1. Codeforces 436E Cardboard Box (看题解)

    Cardboard Box 贪了个半天贪不对, 我发现我根本就不会贪心. 我们先按b排序, 然后枚举选两颗心的b的最大值, 在这个之前的肯定都要选一个, 因为前面的要是一个都没选的话, 你可以把当前选 ...

  2. Codeforces 437C The Child and Toy(贪心)

    题目连接:Codeforces 437C  The Child and Toy 贪心,每条绳子都是须要割断的,那就先割断最大值相应的那部分周围的绳子. #include <iostream> ...

  3. Codeforces Round #546 (Div. 2) D 贪心 + 思维

    https://codeforces.com/contest/1136/problem/D 贪心 + 思维 题意 你面前有一个队列,加上你有n个人(n<=3e5),有m(m<=个交换法则, ...

  4. [CSP-S模拟测试]:trade(反悔贪心)

    题目传送门(内部题62) 输入格式 第一行有一个整数$n$.第二行有$N$个整数:$a_1\ a_2\ a_3\cdot\cdot\cdot a_n$. 输出格式 一行一个整数表示最大收益. 样例 样 ...

  5. 洛谷 P5470 - [NOI2019] 序列(反悔贪心)

    洛谷题面传送门 好几天没写题解了,写篇题解意思一下(大雾 考虑反悔贪心,首先我们考虑取出 \(a,b\) 序列中最大的 \(k\) 个数,但这样并不一定满足交集 \(\ge L\) 的限制,因此我们需 ...

  6. 题解-CF436E Cardboard Box

    题面 CF436E Cardboard Box \(n\) 个关卡,对每个关卡可以花 \(a_i\) 时间得到 \(1\) 颗星,或花 \(b_i\) 时间得到 \(2\) 颗星,或不玩.问获得 \( ...

  7. Codeforces Round #228 (Div. 2) C. Fox and Box Accumulation(贪心)

    题目:http://codeforces.com/contest/389/problem/C 题意:给n个箱子,给n个箱子所能承受的重量,每个箱子的重量为1: 很简单的贪心,比赛的时候没想出来.... ...

  8. CF436E Cardboard Box(贪心)

    题意 有nnn个关卡,第iii关可以花费aia_iai​的代价打一颗星,bib_ibi​的代价打两颗星.保证1≤ai<bi≤1091\le a_i<b_i\le10^91≤ai​<b ...

  9. Codeforces Round #570 (Div. 3) G. Candy Box (hard version) (贪心,优先队列)

    题意:你有\(n\)个礼物,礼物有自己的种类,你想将它们按种类打包送人,但是打包的礼物数量必须不同(数量,与种类无关),同时,有些礼物你想自己留着,\(0\)表示你不想送人,问你在送出的礼物数量最大的 ...

随机推荐

  1. Golang通脉之函数

    函数是组织好的.可重复使用的.用于执行指定任务的代码块. Go语言中支持函数.匿名函数和闭包,并且函数在Go语言中属于"一等公民". 函数定义 Go语言中定义函数使用func关键字 ...

  2. 初识HTML02

    HTML 超文本标记语言 什么是超文本标记语言 浏览器能够解释和解析的语言 通过元素的形式构建页面结构和填充内容 构建HTML页面 构建页面的步骤 创建一个扩展名为.html和.html的页面文件 向 ...

  3. [对对子队]会议记录5.24(Scrum Meeting10)

    今天已完成的工作 梁河览 ​ 工作内容:修改第一关的新手引导 ​ 相关issue:优化初步导出版本 ​ 相关签入:fix:改进第一关的新手引导 何瑞 ​ 工作内容:为加速按钮添加锚点 ​ 相关issu ...

  4. Charles的简单用法

    Charles的简单用法 一.抓电脑上 http 包 二.显示请求的 Request 和 Response 三.抓取电脑上 https 包 1.安装根证书 2.在钥匙串中启用根证书 3.配置哪些需要抓 ...

  5. 热身训练1 Sequence

    http://acm.hdu.edu.cn/showproblem.php?pid=6 分析: 这道题,全都是1e9,所以我们很容易想到"矩阵快速幂". 假如说我们没有后面那个&q ...

  6. 【Azure 应用服务】App Service For Linux 部署Java Spring Boot应用后,查看日志文件时的疑惑

    编写Java Spring Boot应用,通过配置logging.path路径把日志输出在指定的文件夹中. 第一步:通过VS Code创建一个空的Spring Boot项目 第二步:在applicat ...

  7. 结束的NULL

    最近同学叫我帮忙看个问题,为啥这个循环没有退出, 代码如下,原本是想拿到最后的NULL指针就可以结束循环 #include <stdio.h> #include <stdlib.h& ...

  8. live555 rtsp直播卡顿马赛克优化

    最近搞了个rtsp直播,初步是能用了,但是最终效果不是很好,客户不接受要求我们一定要继续优化. 原因是他们体验的时候发现会概率性出现马赛克和画面卡顿情况,经过我们测试验证,确实是有这个问题存在. 从原 ...

  9. Luogu P1654 OSU! | 期望

    题目链接 很妙的一道题. 题目要求$X^3$的期望值. 直接求不好求. 考虑先求出$X$和$X^2$的期望值,然后再求$X^3$的期望值. 迎.刃.而.解. #include<iostream& ...

  10. shell 中单引号和双引号的区别

    用以下代码来说明: #!/bin/bash url="http://c.biancheng.net" website1='C语言中文网:${url}' website2=" ...