题目链接
http://codeforces.com/contest/614/problem/D
题目大意
有n项技能, 每项技能的等级为ai, 技能最大等级为A,
现在有m个技能点(一个技能点可以将一个技能升一级, 达到A后就不能再升了), 求能达到的最大能力值
(pow = sum[A](满级技能个数) * cf + min[ai] (最小技能等级)*cm)
思路
分析题目时我们发现有一个支出和收益的问题,深入思考我们就能得到获得i个A所需技能点个数的关系,以及将最小值提升到ai所需技能点个数的关系,有了这两个关系,我们就可以枚举cf来计算最值了
实现
- 由于m太大, 枚举m必然超时,所以我们枚举A的个数
- 记录一个前缀和sum数组,将ai排序
- 那么获得i个A所需技能点个数=i*A - (sum[n]-sum[n-i])
- 将最小值提升到ai所需要的技能点个数=i*a[i] - sum[i]
- 可以用二分找最小值,上式中的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;
}