HDU 6058

题意略。

思路:这题要求第k大之和,我们可以算每个数字对答案的贡献。这次,我们采取一个特殊的手法:我们按数字的大小,从小到大来计算贡献。

先从1开始计算,我们知道,1周围的数字都比1大,假设1在位置p,那么它能做出贡献的区间是:[p - k + 1,p],[p - k + 2,p + 1],...,[p,p + k -1]。

如果每一个数字都能像1这样就好了,从而我们想到用一个双向链表来维护这个体系,每计算完一个数字我们就把这个数字从我们的链表中删去,

这样就可以总是保证当前计算的那个数字周围的数都比它大(有点像离线的思想)。

详见代码:

#include<bits/stdc++.h>
typedef long long LL;
const LL maxn = 5 * 1e5 + 5;

int lft[maxn],rht[maxn],mp[maxn];
int store1[100],store2[100],s,t;
int n,k;

void dlt(int pos){
    int l = lft[pos],r = rht[pos];
    rht[l] = r,lft[r] = l;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        int temp;
        for(int i = 1;i <= n;++i){
            lft[i] = i - 1,rht[i] = i + 1;
            scanf("%d",&temp);
            mp[temp] = i;
        }
        LL ans = 0;
        for(int i = 1;i <= n;++i){
            memset(store1,-1,sizeof(store1));
            memset(store2,-1,sizeof(store2));
            s = 0,t = 0;
            int pos = mp[i];
            for(int j = 0,ptr = pos;j < k + 1 && ptr;ptr = lft[ptr],++j)
                store1[++s] = ptr;
            for(int j = 0,ptr = pos;j < k + 1 && ptr != n + 1;ptr = rht[ptr],++j)
                store2[++t] = ptr;
            for(int j = s;j >= 1;--j){
                LL L,R;
                int l1 = store1[j],l2 = store1[j + 1];
                L = (l2 == -1 ? l1 : l1 - l2);
                int r1 = store2[k - j + 1],r2 = store2[k - j + 1 + 1];
                if(r1 == -1) R = 0;
                else R = (r2 == -1 ? n - r1 + 1 : r2 - r1);
                ans += L * R * i;
            }
            dlt(pos);
        }
        printf("%lld
",ans);
    }
    return 0;
} 
原文地址:https://www.cnblogs.com/tiberius/p/8682196.html