HDU 6058 Kanade's sum (区间第k大的数的贡献)

思路:

维护一个链表,初始状态为输入的序列。
然后从1开始到n,对每个数统计他在哪些区间中,然后乘以这个数,作为这个数做出的贡献。
统计他在某些区间的办法:

对于每个数i,我们向左找k-1个比他大的数(由于我们是从小到大处理的,且每个数处理后就在链表里删除了,所以这里我们直接一个个跳就好)。假设这第k-1个数为b,再向左找第k个为a,那么我们可以得知,(a,b】中的数皆可作为满足条件的区间的左端点。然后相似的考虑右端点,在这种情况下i的右边第一个比i大的数左边的数都可以作为满足条件的区间的右端点。
然后我们将区间整体向右走一跳。变成左边有k-2个比i大的,右边有1的比i大的,然后同理统计。
区间递推知道左端点为i。
至此我们将i对所有它出现的区间都统计了贡献。
然后我们就可以在链表里删除这个数。
然后接着处理下一个。

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long int lli;
using namespace std;
struct pp{
    int tol,tor;
}head[600000];
int lis[600000];
lli a[600000];
// 自适应辛普森
int main(){
//    freopen("1003.in","r",stdin);
//    freopen("my.txt","w",stdout);
    int cas,n,k,v;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d%d",&n,&k);
        for(int i = 1;i <= n;i++){
            scanf("%d",&v);
            lis[i] = v;
            a[v] = i;
            head[i].tol = i-1;
            head[i].tor = i+1;
        }
        head[0].tor = 1;head[n+1].tor = 0;
        lis[0] = 0;a[0] = 0;
        lis[n+1] = n+1;a[n+1] = n+1;
        lli ans = 0;
        for(int i = 1;i <= n;i++){
            int n1 = a[i],n2,n3 = a[i],j;
            for(j = 1;j <= k-1 && n1;j++){
                n1 = head[n1].tol;
            }
            if(!n1){
                j--;
                n1 = head[n1].tor;
                for(;j <= k-1 && n3!=n+1;j++){
                    n3 = head[n3].tor;
                }
                if(j != k) break;
            }
            for(int cnt = 1;cnt <= k && n1 <= a[i] &&n3;cnt++){
                if(n3 != n+1)
                    ans += (lli)i*(lli)(a[lis[n1]]-a[lis[head[n1].tol]])*(lli)(a[lis[head[n3].tor]]-a[lis[n3]]);
                n1 = head[n1].tor;   n3 = head[n3].tor;
            }
            n1 = head[a[i]].tol,n2 = head[a[i]].tor;
            head[n1].tor = n2;
            head[n2].tol = n1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值