hdu 6058 Kanade's sum

题意:给出一个从1~n的数列,任意排列,求任意区间的第K大的数之和

思路:把求任意区间第K大转化为对某一个数x而言,作为第K大时对区间数量的贡献;通过预处理先记录下每个数的位置,设置两个数组left[],和right[],分别记录x左边和右边比x大的数的位置,这样对枚举的每一个x而言,只需要扫一遍左k和右k个数即可,如果枚举的时候从大到小,可以发现前面添加的数都是比当前x大的;可以发现对于x而言左边舍弃一个比它大的值,右边添加一个比它的值,区间依旧满足x第k大;

附上借鉴一位大牛写的代码:

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
const int maxn=500005;
typedef long long ll;
int n,k;
int le[maxn],ri[maxn],vis[maxn];
const int read()
{
    int f=1,x=0;
    char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f*=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}

ll sol()
{
    set<int>s;
    set<int>::iterator it;
    s.insert(0);
    s.insert(n+1);
    ll ans=0;
    for(int i=n;i>0;i--)
    {
        int l,r;
        s.insert(vis[i]);
        it=s.find(vis[i]);
        it++;
        r=*it;
        l=le[r];
        le[r]=vis[i];
        ri[vis[i]]=r;
        ri[l]=vis[i];
        le[vis[i]]=l;
        int nowl=vis[i];
        for(int j=0;j<k&&nowl;j++)
            nowl=le[nowl];
        int nowr=nowl;
        for(int j=0;j<k&&nowr!=n+1;j++)
            nowr=ri[nowr];
        for(int j=0;j<k;j++)
        {
            if(nowl==vis[i] || nowr==n+1)
                break;
            ans+=1ll*(ri[nowl]-nowl)*(ri[nowr]-nowr)*i;
            nowl=ri[nowl];
            nowr=ri[nowr];
        }
    }
    return ans;
}

int main()
{
    freopen("input.txt","r",stdin);
    int cas;
    for(scanf("%d",&cas);cas;cas--)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            int x=read();
            vis[x]=i;
            le[i]=0;
            ri[i]=n+1;
        }
        if(k<1)
        {
            printf("0
");
            continue;
        }
        le[n+1]=0;ri[n+1]=n+1;
        ll ans=sol();
        cout<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/MeowMeowMeow/p/7275374.html