题意:
给出一个n和一个k,求1~n的每个区间的第k大的总和是多少,区间长度小于k的话,贡献为0.
分析:
我们只要求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,扫一下就可以知道有几个区间的k大值是x.
我们考虑从小到大枚举x,每次维护一个链表,链表里只有>=x的数,那么往左往右找只要暴力跳k次,删除也是O(1)的。
时间复杂度:O(nk)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500010;
int Case, n, K, num[N], pos[N], pre[N], nxt[N];
inline void del(int x)
{
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
}
inline ll solve(int x)
{
int a[100], b[100];
int ca = 0, cb = 0;
for(int i = x; i; i = pre[i])
{
a[++ca] = i - pre[i];
if(ca == K) break;
}
for(int i = x; i <= n; i = nxt[i])
{
b[++cb] = nxt[i] - i;
if(cb == K) break;
}
ll t = 0;
for(int i = 1; i <= ca; i++)
if(K - i + 1 <= cb)
t += 1LL * a[i] * b[K-i+1];
return t;
}
int main()
{
scanf("%d", &Case);
while(Case--)
{
scanf("%d%d", &n, &K);
for(int i = 1; i <= n; i++) scanf("%d", &num[i]), pos[num[i]] = i;
for(int i = 0; i <= n+1; i++) pre[i] = i-1, nxt[i] = i+1;
pre[0] = 0;
nxt[n+1] = n+1;
ll ans = 0;
for(int i = 1; i <= n; i++)
{
int x = pos[i];
ans += solve(x) * i;
del(x);
}
printf("%I64d\n", ans);
}
return 0;
}