1. 程式人生 > 實用技巧 >Range k-th Maximum Query-2020百度之星複賽

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);
    }
}