HDU 6058 Kanade's sum(思维)

本文介绍一种算法,用于解决给定序列中每个区间第K大数之和的问题。通过记录每个数的位置并使用set维护,实现高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Kanade's sum

Problem Description

Give you an array A[1..n]of length n.

Let f(l,r,k) be the k-th largest element of A[l..r].

Specially , f(l,r,k)=0 if r−l+1<k.

Give you k , you need to calculate ∑nl=1∑nr=lf(l,r,k)

There are T test cases.

1≤T≤10

k≤min(n,80)

A[1..n] is a permutation of [1..n]

∑n≤5∗105 

Input

There is only one integer T on first line.

For each test case,there are only two integers n,k on first line,and the second line consists of n integers which means the array A[1..n]

Output

For each test case,output an integer, which means the answer.

Sample Input

1

5 2

1 2 3 4 5

Sample Output

30

Source

2017 Multi-University Training Contest - Team 3


题意:给出长为n的序列a,给出整数k,求对于序列a,每个区间第k大的数之和

题解:相当于求每个数字分别是多少个区间的第k大记录每个数出现的位置,维护一个set(相当于链表),从大到小遍历,将每个数的位置插入set;对于每个数,只需在其两边找k个比它大的数,对起始位置进行遍历,计算每个数对答案的贡献,求和。


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<math.h>
#include<queue>
#define INF 1e9
#define ll long long
#define maxn 300005
#include<set>
#include<stack>
using namespace std;
int a[500005];
int pos[500005];
int ri[500005];
int le[500005];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        memset(a,0,sizeof(a));
        memset(pos,0,sizeof(pos));
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[a[i]]=i;
            le[i]=0;
            ri[i]=n+1;
        }
        set<int>s;
        set<int>::iterator it;
        s.insert(0);
        s.insert(n+1);
        ll ans=0;
        le[n+1]=0;
        ri[n+1]=n+1;
        ri[0]=n+1;
        le[0]=0;

        for(int x=n;x>=1;x--)
        {
            s.insert(pos[x]);
            it=s.find(pos[x]);
            it++;
            int p=*it;
            int pp=le[p];
            le[pos[x]]=pp;
            ri[pos[x]]=p;
            le[p]=pos[x];
            ri[pp]=pos[x];

            int l=pos[x];
            for(int j=0;j<k&&l!=0;j++)
                l=le[l];
            int r=l;
            for(int j=0;j<k&&r!=n+1;j++)
                r=ri[r];
            int nowl=l;
            int nowr=r;
            for(int j=0;j<k;j++)
            {
                if(nowl==pos[x]||nowr==n+1)
                    break;
                int nextl=ri[nowl];
                int nextr=ri[nowr];
                ans+=(1ll*(nextl-nowl)*(nextr-nowr)*x);
             //   cout<<ans<<endl;
                nowl=nextl;
                nowr=nextr;
            }
        }
       printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值