题解 CF436E 【Cardboard Box】

· · 题解

更狼牙棒的体验 \to George1123

题面

CF436E Cardboard Box

数据范围:$1\le n\le 3\cdot 10^5$,$1\le m\le 2n$,$a_i<b_i$。

题解

蒟蒻做这题时已经知道是反悔贪心了,但是做了 4 个小时做不出来。于是去看题解,感觉这个 5 堆做法太非人类了,于是去看 CF 题解和代码后总结出了蒟蒻的不用反悔贪心的做法和题解。

如果不反悔贪心,那么每一步必须保证没有后效性地最优。

很明显如果只有 a_i,排序可以满足这个条件。

这题最朴素的错误做法是用一个小顶堆,刚开始把所有 a_i 丢进去,然后 m 次每次取堆顶,如果选了 a_ib_i-a_i 也丢进去。

如果 a_i 都很大,b_i-a_i 都很小这样显然是亏的。

假如某个 i 打出 2 星是当前最优,如果 a_i 已经取了按照上面的办法肯定没有问题;但是如果 a_i 都没取,a_i 不一定是单个中的最优。

所以这篇题解最重要的骚操作就来了:选之前权衡,选两个的最优还是一个的最优:哪个更优?

可以用一个 vis 数组记录打了那些关卡,每次把打了的从堆中扔掉,这样就可以假定堆中的都是没有打过的了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=3e5,sN=N<<1;
int n,m,star[N];
ll a[sN],ns; bool vis[sN];
priority_queue<pair<ll,int>> o,t;
void ref(priority_queue<pair<ll,int>> &q){
    while(sz(q)&&vis[q.top().y]) q.pop();
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    R(i,n) cin>>a[i]>>a[i+n],a[i+n]-=a[i],
        o.push(mp(-a[i],i)),t.push(mp(-a[i]-a[i+n],i));
    while(m--){
        ref(o),ref(t);
        int i=o.top().y; o.pop(),ref(o);
        if(m&&sz(t)&&a[i]-o.top().x>=-t.top().x)
            o.push(mp(-a[i],i)),i=t.top().y,t.pop();
        i<n&&(o.push(mp(-a[i+n],i+n)),true);
        ns+=a[i],star[i%n]++,vis[i]=true;
    }
    cout<<ns<<'\n';
    R(i,n) cout<<star[i],i==n-1&&(cout<<'\n');
    return 0;
}

/*

可以考虑不反悔:权衡一个两个哪个优!

*/

祝大家学习愉快!