hdu--6058Kanade's sum

   先是比赛的时候理解错了题意。。第K大惯性思维就是从小数第k个数,样例真是水到爆炸。。原来第k大是从大往小数第K个数,,我记得上次练习主席树和树状数组的时候也为这个纠结了一会,没想到还是没反应过来,其次,比赛的时候其实是想到了正解的,当初没办法验证时间复杂度呀,忘记了链表这玩意了呀 ,所以就GG了,水平还是很菜呀,最近也是被主席树和线段树坑到了,一心想要用主席树上套。。虽然已经知道完全不可能。。

  看了一波题解,证明了思路是不会超时的之后,这个题就很好解决 啦,给的k最大只有80,然后数列里的数又是1到n,那么就直接从小到大枚举每一个数,是枚举i,然后可以通过pos[i]确定它在数组中的位置,nex[i]相当于往后的链表,记录该数的下一个数的位置,pre[i]就是往前找的。枚举的时候我们从i的左边找k个比i大的数,(也许没有K个就到了数组的边界,这里要特殊处理),再从右边找K个比i大的数(同理),那么从左边第(K-1)个位置带i这个数组所在的位置pos[i],恰好是满足条件的第一个区间(如果左边不足k个,那么就需要从右边补上K-X个区间,以满足条件),那么从左边到右边恰好满足了条件,再从左边第K-1个数所在的位置到第K个数所在的位置中间的一段空白的数必然是小于i的,那么这些数也可以一个个加入区间并且满足条件,同理左边的那段空白的数也满足条件,最后就是要加一个边界条件,以免去判断的条件,由于所有的数都是在1到N,我们可以将gp[n+1]=n+1,gp[0]=n+2;,这样,在往前往后走的时候,结尾必然是一个大于i的数 ,具体细节看代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define siz 500005
#define LL long long

using namespace std;
int n,gp[siz],k;
int go_left[siz],go_right[siz];
int pos[siz],pre[siz],nex[siz];
void _Init(){
    for(int i=0;i<siz;i++){
        go_left[i]=go_right[i]=pos[i]=-1;
        pre[i]=nex[i]=-1;
    }
}
void solve(){
     LL ans=0;
     for(int i=1;i<=n;i++){
        int len_r=1,j=nex[pos[i]];
        go_left[0]=go_right[0]=i;
        while(len_r<=k&&j<=n+1){
            if(gp[j]>i){
                go_right[len_r++] = gp[j];
            }
            j=nex[j];
        }
        int len_l=1;
        j=pre[pos[i]];
        while(len_l<=k&&j>=0){
            if(gp[j]>i){
                go_left[len_l++] = gp[j];
            }
            j=pre[j];
        }
        int le= len_l - 2;
        int ri = k - (len_l - 1);
        while(le>=0&&ri<len_r-1){
            ans += (LL)((LL)(pos[go_left[le]] - pos[go_left[le+1]]) *
            (LL)(pos[go_right[ri+1]] - pos[go_right[ri]])) *1LL* i;
            --le,++ri;
        }
        j=pos[i];
        int pr = pre[j];
        int later = nex[j];
        pre[later] = pr;
        nex[pr] = later;
     }
     printf("%lld\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&gp[i]);
            pos[gp[i]]=i;
            pre[i]=i-1;
            nex[i]=i+1;
        }
        pre[0]=-1;
        nex[n+1]=n+2;
        gp[n+1]=n+1,gp[0]=n+2,pos[n+1]=n+1,pos[n+2]=0;
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值