题解:AT_abc389_e [ABC389E] Square Price

前言

E 比 F 困难,怎么会是呢?

思路分析

首先考虑一种经典做法:将问题转化为物品体积为 \(p_i,3p_i,5p_i,7p_i \cdots\) 的 01 背包问题。因为物品价值都为 \(1\),所以可以贪心地选择体积前 \(k\) 小的物品。

然后问题转化到这一步就自然了,考虑二分出我们选与不选的物品体积的临界值,小于等于这个值的物品都选上。

已知临界值 \(mid\),对于每一个 \(p_i\),我们选择的物品数量 \(k\) 应该满足:

\[(2k-1)p_i\le mid \]

放缩一下:

\[k \le \lfloor \frac{\lfloor \frac{mid}{p_i} \rfloor +1}{2} \rfloor \]

这里有一个细节:在选完小于等于临界值的物品后,如果背包容量还有剩余,会考虑选择大于临界值的物品,特判一下即可。

总体复杂度 \(O(n \log m)\)

代码实现

不知道在 check 时会不会炸 long long,所以开了 __int128。

#include<bits/stdc++.h>
using namespace std;
long long ans,p[200005],sum,m,n,maxn;
bool check(long long ans){
	__int128 sum=0;
	for(int i=1;i<=n;i++){
		__int128 num=((ans/p[i]+1))/2;
		sum+=num*num*p[i];
		if(sum>m) return false;
	}
	return true;
}
long long find(long long l,long long r){
	if(l==r) return l;
	long long mid=(l+r+1)>>1;
	if(check(mid)) return find(mid,r);
	else return find(l,mid-1);
}
int main(){
	//freopen("in.in","r",stdin);
	//freopen("out.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(long long i=1;i<=n;i++){
		cin>>p[i];
	}
	ans=find(0,m);
	for(long long i=1;i<=n;i++){
		__int128 num=(ans/p[i]+1)/2;
		sum+=num;
		m-=num*num*p[i];
		maxn=max((__int128)maxn,(2*num+1)*p[i]);
	}
	sum+=m/maxn;
	cout<<sum;
	return 0;
}
posted @ 2025-01-24 16:45  _Kenma  阅读(68)  评论(0)    收藏  举报