Codeforces Round #339 (Div. 2)D. Skills(二分)

题目链接

http://codeforces.com/contest/614/problem/D

题目大意

有n项技能, 每项技能的等级为ai, 技能最大等级为A,
现在有m个技能点(一个技能点可以将一个技能升一级, 达到A后就不能再升了), 求能达到的最大能力值
(pow = sum[A](满级技能个数) * cf + min[ai] (最小技能等级)*cm)

思路

分析题目时我们发现有一个支出和收益的问题,深入思考我们就能得到获得i个A所需技能点个数的关系,以及将最小值提升到ai所需技能点个数的关系,有了这两个关系,我们就可以枚举cf来计算最值了

实现

  1. 由于m太大, 枚举m必然超时,所以我们枚举A的个数
  2. 记录一个前缀和sum数组,将ai排序
  3. 那么获得i个A所需技能点个数=i*A - (sum[n]-sum[n-i])
  4. 将最小值提升到ai所需要的技能点个数=i*a[i] - sum[i]
  5. 可以用二分找最小值,上式中的i可以用lower_bound找比mid小的技能有几个

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+100;
ll n, A, cf, cm, m, sum[maxn];
struct P
{
    ll a, id;
    P(ll x, ll y) :a(x), id(y){};
    P(){}
    bool operator<(const P &x) {return a < x.a;}
}p[maxn];

int main()
{
    cin >> n >> A >> cf >> cm >> m;
    for(int i=0; i<n; ++i)
    {
        scanf("%I64d", &p[i].a);
        p[i].id = i;
    }
    sort(p, p+n);
    for(int i=0; i<n; ++i) sum[i+1] = sum[i]+p[i].a;
    int tcnt = 0;
    for(int i=0; i<n; ++i) if(p[i].a == A) ++tcnt;
    ll ans = tcnt*cf + p[0].a*cm;
    int cntm = 0;
    int mi = 0;
    for(int i=0; i<=n; ++i)
    {
        ll x = m - (A*i-(sum[n]-sum[n-i]));
        if(x<=0) break;
        ll l=0, r=A, mid;
        while(l<r)
        {
            mid = (l+r)/2;
            int cnt = lower_bound(p, p+n-i, P(mid, 0ll)) - p;
            if(cnt*mid-sum[cnt] <= x) l = mid+1;
            else r = mid;
        }
        int tcnt = lower_bound(p, p+n-i, P(l, 0ll))-p;
        if(tcnt*l-sum[tcnt] > x) --l;
        ll tans = i*cf + l*cm;
        if(tans > ans)
        {
            ans = tans;
            cntm = i;
            mi = l;
        }
    }
    for(int i=n-1; i>=0 && cntm; --i, --cntm) p[i].a = A;
    for(int i=0; i<n; ++i) if(p[i].a < mi) p[i].a = mi;
    sort(p, p+n, [](const P&x, P&y) {return x.id < y.id; });
    cout << ans << endl;
    for(int i=0; i<n; ++i) printf("%I64d ", p[i].a);
    cout <<endl;


    return 0;
}

总结

  • 系统自带的lower_bound和upper_bound 返回的是指针而不是位置下标int
  • 类似的题还有AtCoder Regular Contest 077 E - guruguru
    像这些题目中有收益与支出问题的,我们可以先尝试得到他们的关系再进行下一步的思考

问题

首先这道题的思路是在队友的一丝指导下想出来的,但是没想到还是在实现上出了问题,一开始我并没有用二分去找最小值,而是直接用一个数组记录了把最小值提升到ai所需要的技能点个数,总之这样问题很多
后来参考题解用二分结果还是写挫了,至今未找出bug,甚至怀疑题解二分的一种情况:技能点不够时lower_bound找到的比它小的个数是0,结果左边界l一直上升到a?
不得不承认这题细节很多,现在暂时很难一下查出bug,先放着

//这是我的错误代码
#include<bits/stdc++.h>
using namespace std;

const int M = 1e5 + 5;
typedef long long ll;
struct P
{
    ll id, val;
    P(){}
    P(ll x, ll y):id(y), val(x){};
    bool operator < (const P& a)
    {
        return val < a.val;
    }
}num[100005];
ll cnt[M], sum[M];

int main()
{
    ll n, a, cf, cm, m, tot = 0;
    cin>>n>>a>>cf>>cm>>m;
    for(int i=1; i<=n; ++i)
    {
        int v;
        scanf("%d", &v);
        num[i] = P(v, i);
        sum[i] = v;
        if(v == a) ++tot;
    }
    sort(num+1, num+n+1);
    for(int i=2; i<=n; ++i)
        sum[i] += sum[i-1];
    ll ans = num[1].val*cm + tot*cf, mi=0, mt=num[1].val;
    for(int i=0; i<=n; ++i)
    {
        ll mm;
        if(i==0)mm = m;
        else mm = m - (i*a - (sum[n]-sum[n-i]));
        if(mm < 0)break;
        ll l = 0, r = a, mid;
        while(l < r)
        {
            mid = (l+r)/2;
            ll tmp = lower_bound(num+1, num+n+1-i, P(mid, 0)) - (num+1);
            if(tmp*mid - sum[tmp] <= mm) l = mid+1;
            else r = mid;
        }
        int tmp = lower_bound(num+1, num+n+1-i, P(l, 0)) - (num+1);
        if(tmp*l - sum[tmp] > mm) --l;
        //cout<<l<<endl;
        ll tans = i * cf + l * cm;
        if(tans > ans)
        {
            ans = tans;
            mt = l;
            mi = i;
        }
    }
    //cout<<mt<<' '<<mi<<endl;
    for(int i=1; i<=n; ++i)
    {
        if(num[i].val < mt)cnt[num[i].id] = mt;
        else if(i>n-mi) cnt[num[i].id] = a;
        else cnt[num[i].id] = num[i].val;
    }
    cout<<ans<<endl;
    for(int i=1; i<=n; ++i)
    {
        cout<<cnt[i]<<' ';
    }
    cout<<endl;
    return 0;
}
发布了54 篇原创文章 · 获赞 13 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章