2017 Multi-University Training Contest 3 1003/hdu6058

题意:给你个1-n的排列,找到每一个区间第k大的数,求这些数加起来的总和,如果区间长度小于k,值就为0。

开始一直想用主席树解,但是复杂度太高,不能做,思路卡的时候还是要多转换下思路才行。

解法:找第k大数可以转换为,从小到大枚举x,找一个数x的左边大于x的y个数,右边大于x的k-y-1个数的区间有多少个,然后乘以x然后一起加起来就得到答案了。主要使用双向链表这个神奇的东西,如果前面的数的贡献算完了,那么就可以删除它,因为是从小到大枚举,所以它对后面的区间计算没有贡献。


#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <string.h>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#define LL long long
#define INF 0x3f3f3f3f
const int MAX_N = 5e5+10;
const int mod = 100;
const double eps = 1e-6;
using namespace std;
LL a[MAX_N],b[MAX_N];
int pos[MAX_N],pre[MAX_N],nxt[MAX_N];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k,x;
        scanf("%d%d",&n,&k);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&x);
            pos[x] = i;
            pre[i] = i-1;
            nxt[i] = i+1;
        }
        pre[0] = 0; nxt[n+1] = n+1;
        LL ans = 0;
        int l,r;
        for(int i = 1;i <= n;i++)
        {
            l = r = 0;
            x = pos[i];
            for(int j = x;l <= k&&j >= 1;j = pre[j])
                a[++l] = j-pre[j];
            for(int j = x;r <= k&&j <= n;j = nxt[j])
                b[++r] = nxt[j]-j;
            LL tmp = 0;
            for(int j = 1;j <= l;j++)
                if(k-j+1 >=1&&k-j+1 <= r)
                    tmp+=a[j]*b[k-j+1];
            ans+=tmp*i;
            pre[nxt[x]] = pre[x];
            nxt[pre[x]] = nxt[x];
        }
        cout<<ans<<endl;
    }
    return 0;
}
/*
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值