给定每个物品(无限多个) ,各具有独特的价值 ,现在一共有 块钱,第物品选 个的代价为 ,问最多可以选多少个物品。
第一眼看像是个变种背包,但是即使拆分之后去做背包还是会爆掉,所以舍弃了这个思路,转而思考贪心。
如果一个物品已经购买了 个,那么再购买一个,所花的代价就变成了 ,增量为 。那么很浅显的一个贪心就是根据每个物品的增量进行一个优先队列中的选取,选取完了之后再改变其属性值,然而这个复杂度并不是特别好分析,不过感觉上并不高,就写了一下,果不其然T了。
考虑优化,这个选取的过程其实不必一个一个选,可以通过二分来确定当前物品选的最大个数,不过这仍然是杯水车薪,交上去还是T了。
只好求助题解了,考虑 的特殊性,我们已经分析出来了 的增量,那么选第 个物品的代价依次就是 ,其总代价(前缀和)刚好就是 ,然而这一步还是基本上能够自己想到,但是不知道后面怎么做了。
所有的上述代价合并起来排个序,形成一个新的序列,那么我们肯定会从小到大选取尽可能多的拆分后物品。
因此一定有一个分界点,我们选取了分界点之前的所有物品,并且达到了选取上限,所以我们去二分一下所选取物品的最大价格 ,那么 初步来说满足 的所有物品都会选取,我们可以 算出在这类物品上花费的总价格,总共 就能 check 完。
复杂度 。
然而还是过不了,为什么?
考虑以下 hack。
拆分以后会发现 只能选一个,然而代码会判断为一个都选不了,因为 "满足 的所有物品都会选取"这个结论本身有问题,和“分界点”并不等价。所以我们还要在最后输出答案的时候加上剩下的钱能够购买的东西。
| #include<bits/stdc++.h> |
| using namespace std; |
| #define int long long |
| const int MX=1e18; |
| int n,m; |
| const int N=2e5+10; |
| int a[N]; |
| const double eps=1e-9; |
| inline bool check(double x) |
| { |
| long long nowval=0; |
| for(int i=1;i<=n;++i) |
| { |
| double tmp=(x+a[i])/(2ll*a[i]); |
| int t=(int)floor(tmp); |
| if(1.0*t-(1.0*(m-nowval)/(1.0*t)/(1.0*a[i]))>eps)return 0; |
| nowval+=t*t*a[i]; |
| } |
| return 1; |
| } |
| inline void solve() |
| { |
| cin>>n>>m; |
| for(int i=1;i<=n;++i)cin>>a[i]; |
| int l=0,r=m; |
| while(l<r) |
| { |
| int mid=(l+r+1)>>1; |
| if(check(mid*1.0))l=mid; |
| else r=mid-1; |
| } |
| long long ans=0; |
| double x=1.0*l; |
| for(int i=1;i<=n;++i) |
| { |
| double tmp=(x+a[i])/(2ll*a[i]); |
| int t=(int)floor(tmp); |
| m-=t*t*a[i],ans+=t; |
| } |
| cout<<ans+m/(l+1)<<'\n'; |
| } |
| signed main() |
| { |
| ios::sync_with_stdio(0); |
| cin.tie(0),cout.tie(0); |
| solve(); |
| |
| return 0; |
| } |
其实根据 巨大的范围应该猜到复杂度里面多半带个 (因为不是结论题)。
那很自然地就应该去想到二分一下价格。
所以还是要多参考数据范围进行解题。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!