HDU 6058 Kanade's sum 逆序求第k大

传送门:HTTP://acm.hdu.edu.cn/showproblem.php PID = 6058

题意:给你一个1-n的排列求所有自子区间的第K大的和

思路:从大到小插入位置维护当前数位置以及当前数前面比他大的个数及位置后面比他大的及位置所以k = min(n,80)链表维护在保证区间有k-1个比当前大的情况计算贡献手写链表维护位置

码:

 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline void read(int &x)
{
    x = 0;
    char p = getchar();
    while(!(p <= '9' && p >= '0'))p = getchar();
    while(p <= '9' && p >= '0')x *= 10, x += p - 48, p = getchar();
}
const int MAXN = 5e5 + 10;
struct node
{
    int fro, bac;
} pos[MAXN];
int a[MAXN], p[MAXN], afro[90], abac[90];
int main()
{
    int t;
    read(t);
    for(int cas = 1; cas <= t; cas++)
    {
        int n, k;read(n), read(k);
        for(int i = 0; i <= n + 1; i++)  pos[i].fro = 0, pos[i].bac = n + 1;
        set<int>s;s.insert(0), s.insert(n + 1);
        set<int>::iterator ite;
        for(int i = 1; i <= n; i++)
        {
            read(a[i]);
            p[a[i]] = i;
        }
        LL ans = 0;
        for(int i = n; i >= 1; i--)
        {
            ite = s.lower_bound(p[i]);
            int fro = *(--ite), bac = *(++ite);
            s.insert(p[i]);
            pos[fro].bac  = p[i], pos[p[i]].fro = fro;
            pos[p[i]].bac = bac , pos[bac].fro  = p[i];
            fro = bac = p[i];
            if(i > n - k + 1) continue;
            ///计算前k-1 后k-1位置
            int fron = 0, bacn = 0;
            for(int j = 1; j <= k ; j++)
            {
                afro[j] = pos[fro].fro;
                fro = pos[fro].fro;
                if(fro == 0 || fron == k - 1) break;
                fron++;
            }
            for(int j = 1; j <= k; j++)
            {
                abac[j] = pos[bac].bac;
                bac = pos[bac].bac;
                if(bac == n + 1 || bacn == k - 1) break;
                bacn++;
            }
            afro[0] = p[i], abac[0] = p[i];
            ///计算i对答案的贡献
            for(int j = fron; j >= 0; j--)
            {
                if(k - j - 1 > bacn) break;
                LL x1=afro[j] - afro[j + 1],x2=abac[k - j ] - abac[k - j -1],x3=i;
                ans += x1*x2*x3;
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值