HDU6058 Kanade's sum

【题目链接】
http://acm.hdu.edu.cn/showproblem.php?pid=6058

题目意思

给你长度为n的a数组,问你数组a组成的区间第k大的总和。

解题思路

确定a数组一个元素x,判断x右边是否有k比x大的元素,没有左边补。找到右边第k个比x大的数a[j](或小于k),如果a[j]右边还有y个比x小的数,就多y个区间满足第k大。右边找找完往左找,每找到一个比x大数,右边比x的退一个,也就是去掉a[j],到a[j-1]结束,但是要加上a[j]到a[j-1]中比x小的个数。。不断往左找直到k个比x大的都到左边为止

代码部分

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef  long long LL;
const LL mod = 1e9+7;
int a[N], b[N], pos[N];

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, k;
        scanf("%d %d", &n, &k);
        for(int i=1; i<=n; i++)  scanf("%d", &a[i]);
        LL ans=0;
        for(int i=1; i<=n; i++)
        {
            int x=a[i], cnt=0, j, sum=0;
            b[++cnt]=i;
            for(j=i+1; j<=n&&cnt<k; j++)  ///先往右找
                if(a[j]>x)  
                    b[++cnt]=j;    ///记录比a[i]大的数字位子
            if(cnt==k)   ///当找到k个比a[i]大的数时,也啊a[i]为第k大
            {
                int num=1;
                for(; j<=n; j++)  ///继续往右有多少个比a[i]小的数就可以组成多少那个区间
                {
                    if(a[j]<x) num++;
                    else break;
                }
                sum+=num;
                for(j=i-1; j>=1&&cnt>=1; j--)///开始往左找
                {
                    if(a[j]<x) sum+=num;  ///比a[i]小就而外多个区间
                    else ///如果比a[i]大就把最右边比a[i]大的数去掉
                    {
                        if(cnt==1) break;
                        num=b[cnt]-b[cnt-1];  ///加上最右边和倒二之间比a[i]小的个数
                        cnt--;
                        sum+=num;
                    }
                }
            }
            else ///如果右边没法找到k个比a[i]大的数
            {
                for(j=i-1; j>=1&&cnt<k; j--)///左边来凑
                    if(a[j]>x) 
                        b[++cnt]=j;
                if(cnt==k)
                {
                    sort(b+1,b+cnt+1);  ///下标排序
                    int num=n-b[cnt]+1;  ///加上最右边比a[i]大的数后面比a[i]小的数
                    sum+=num;
                    for(; j>=1&&b[cnt]>=i; j--) ///继续左移右减
                    {
                        if(a[j]<x) sum+=num; ///看上面
                        else
                        {
                            if(b[cnt]==i) break;
                            num=abs(b[cnt]-b[cnt-1]);
                            cnt--;
                            sum+=num;
                        }
                    }
                }
            }
            ans+=(LL)x*sum;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值