2020百度之星复赛1003-Range K-th Maximum Query

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6840
这里直接证明构造的正确性,首先使和最大,那么假如 l=8,k=3。那么我们可以这样构造,0000011100000111…,1的位置按a[]数组从大到小排序,然后依次填到1的位置,我们发现所有111里的第3个1,会贡献l-k+1次,除了第一个111外,其它的111的第一个1和第二个1会贡献1次,这样就保证了最大。因为,a1…an是从大到小,那么第一个111中的第3个1便是第三大的数,让其贡献尽可能多,这样取肯定是最大的。然后依次是第六大,第九大贡献尽可能多。我们可能会想,为什么不让第四大贡献尽可能多呢?因为如果使第四大贡献尽可能多,这样就无法让第三大贡献尽可能多了。
至于和最小,其实是其对偶问题,使其这样构造便可以,0011111100111111,然后1里依次填a[]从小到大的数。这样构造,每次就会使第6…12…18小的贡献尽可能多。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int T,n,l,k;
ll a[maxn];
ll sum[maxn];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&l,&k);
        for(int i=1;i<=n;++i)
        scanf("%lld",&a[i]);
        sort(a+1,a+1+n,greater<int>());
        ll ans1=0,ans2=0;
        int t=n-l+1;
        for(int i=k;i<=n;++i)
        {
            if(i%k==0)
            {
                ans1+=a[i]*min(t,l-k+1);
                t-=l-k+1;
            }
            else
            {
                ans1+=a[i];
                t--;
            }
            if(t<=0)
            break;
        }
        sort(a+1,a+1+n);
        t=n-l+1;
        for(int i=l-k+1;i<=n;++i)
        {
            if(i%(l-k+1)==0)
            {
                ans2+=a[i]*min(t,k);
                t-=k;
            }
            else
            {
                ans2+=a[i];
                t--;
            }
            if(t<=0)
            break;
        }
        printf("%lld %lld\n",ans1,ans2);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/108012896
今日推荐