Skip to main content
Original282 words1 min

题解:P17831 [CSP-S 2026] 能量水晶

Summary

水晶只能增加能量,选定一组后,将它们全部增加到组内最大值的代价最小。排序后固定数量和最大下标,用更靠近最大值的元素替换较小元素不会增加代价,因此只需考虑连续区间。利用区间和计算操作次数,并按右端点递增维护最长可行窗口,得到 O(nlogn) 的排序与双指针算法。

解题思路​

将能量值从小到大排序。固定选取数量 kk 和选中元素的最大下标 rr,最优目标值为 ara_r。若选中了 r−k+1r-k+1 之前的元素,则区间 [r−k+1,r][r-k+1,r] 内必有未选中的元素。用后者替换前者,操作次数不会增加。因此,仅需考虑连续区间。

设区间 [l,r][l,r] 的元素和为 ss,将其全部增加到 ara_r 的代价为:

∑i=lr(ar−ai)=(r−l+1)ar−s\sum_{i=l}^r(a_r-a_i)=(r-l+1)a_r-s

固定 ll,右移 rr 时,原有元素的目标值不会降低,新元素无需操作,故代价不减。于是依次枚举 rr,若代价超过 mm,便右移 ll,直到区间可行。被舍弃的左端点以后仍不可行,故此时的 [l,r][l,r] 是以 rr 为右端点的最长可行区间。取所有区间长度的最大值即可。

两个指针均至多移动 nn 次,总时间复杂度为 O(nlog⁡n)O(n\log n)。代价不超过 (n−1)×2×1012<1018(n-1)\times2\times10^{12}<10^{18},使用 long long。

参考代码​

415 Bcpp
#include <bits/stdc++.h>
using namespace std;

using ll=long long;
const int N=500005;
ll a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
ll m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
int l=1,ans=0;
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=a[i];
while(a[i]*(i-l+1)-sum>m)
{
sum-=a[l];
l++;
}
ans=max(ans,i-l+1);
}
cout<<ans<<'\n';
return 0;
}