前言
大概是一個經典題,只不過比較難想
思路
首先考慮 \(O(k)\) 的做法,很明顯,每次我們可以選取一個最大值,然後在把他放回優先佇列裡面,只不過這樣不足以透過此題
而我們又發現只要我們知道最後一次選取的數(第 \(k\) 大)是多少,則前面的數全都可以知道(即對於每個 \(a_i\) ,看比這個數大的數,用等差數列求和)
其實第 \(k\) 大的數明顯具有單調性,判斷第 \(k\) 大隻需要看每個 \(a_i\) 比 \(mid\) 大的數的個數即可
在最後統計答案時候需要注意,可能第 \(k\) 大的數有多個,我們可能只需要選取其中的幾個,統計答案時候需要注意
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N],b[N];
int n,k;
bool check(int mid)
{
long long cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=mid)cnt=cnt+1LL*(a[i]-mid)/b[i]+1;
}
return cnt>=k;
}
int main()
{
int _;
cin>>_;
while(_--)
{
cin>>n>>k;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
int l=0,r=1e9,best=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}//求第 k 大
//best 即為第 k 大
if(best==-1)//特判選不完的情況
{
long long ans=0;
for(int i=1;i<=n;i++)
{
long long siz=a[i]/b[i]+1;
long long h=a[i]-(siz-1)*b[i];
ans=ans+1LL*(h+a[i])*siz/2;
}
cout<<ans<<"\n";
continue;
}
long long ans=0;
long long sum=0;
for(int i=1;i<=n;i++)
{
if(a[i]<best) continue;
long long cnt=(a[i]-best)/b[i]+1;
long long h=(a[i]-(cnt-1)*b[i]);
if(h==best) h+=b[i],cnt--;//先選擇大於第 k 大的數
ans=ans+1LL*(h+a[i])*(cnt)/2;
sum=sum+1LL*cnt;
}
//cout<<sum<<"\n";
ans=ans+1LL*best*(k-sum);//若不足則用第 k 大補齊即可
cout<<ans<<"\n";
}
}
/*
1
5 1000
1 2 3 4 5
5 4 3 2 1
*/