Range k-th Maximum Query-2020百度之星复赛
题意
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6840
分析:
最小等价于每个区间找第 \(l+1−k\) 小,然后使得和最小,和最大基本等价,所以我们考虑最大。最优方案中,假设 \(l=8,k=3\),那么一定会这么填 \(000001110000011100000111...\) 从大到小依次给这些位置 \(1\) 的位置填上数字。证明大概可以考虑如果我们放了前 \(a\) 大的,使得尽量多第 \(k\) 大的大于等于这个数的尽量多,那么就等价于放 \(a\) 个 \(1\) ,使得不小于 \(k\) 个 \(1\) 的区间尽量多。
代码实现上,按段处理,每段长度为 \(l\),对于每段中末尾长度为 \(k\)的部分,分别求出每个数的贡献次数。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int a[N],n,l,k;
bool cmp(int x,int y)
{
return x>y;
}
ll solve()
{
ll ans=0;
int d=n/l;
int r=n-d*l;
for(int i=k;i<d*k;i++)
{
int p=n-i+1;
if(i%k==0) ans+=1LL*(l-k+1)*a[p];
else ans+=a[p];
}
ans+=1LL*(min(r+1,l-k+1))*a[n-d*k+1];
if(r>l-k)
{
for(int i=1;i<=r-(l-k);i++)
ans+=a[n-k*d-i+1];
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&l,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
ll ans=solve();
printf("%lld ",ans);//最大值
k=l-k+1;//第l-k+1小
sort(a+1,a+1+n,cmp);//按相反的顺序
ans=solve();
printf("%lld\n",ans);
}
}