题意略。
思路:这题要求第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; }