传送门:HTTP://acm.hdu.edu.cn/showproblem.php PID = 6058
题意:给你一个1-n的排列求所有自子区间的第K大的和
思路:从大到小插入位置维护当前数位置以及当前数前面比他大的个数及位置后面比他大的及位置所以k = min(n,80)链表维护在保证区间有k-1个比当前大的情况计算贡献手写链表维护位置
码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline void read(int &x)
{
x = 0;
char p = getchar();
while(!(p <= '9' && p >= '0'))p = getchar();
while(p <= '9' && p >= '0')x *= 10, x += p - 48, p = getchar();
}
const int MAXN = 5e5 + 10;
struct node
{
int fro, bac;
} pos[MAXN];
int a[MAXN], p[MAXN], afro[90], abac[90];
int main()
{
int t;
read(t);
for(int cas = 1; cas <= t; cas++)
{
int n, k;read(n), read(k);
for(int i = 0; i <= n + 1; i++) pos[i].fro = 0, pos[i].bac = n + 1;
set<int>s;s.insert(0), s.insert(n + 1);
set<int>::iterator ite;
for(int i = 1; i <= n; i++)
{
read(a[i]);
p[a[i]] = i;
}
LL ans = 0;
for(int i = n; i >= 1; i--)
{
ite = s.lower_bound(p[i]);
int fro = *(--ite), bac = *(++ite);
s.insert(p[i]);
pos[fro].bac = p[i], pos[p[i]].fro = fro;
pos[p[i]].bac = bac , pos[bac].fro = p[i];
fro = bac = p[i];
if(i > n - k + 1) continue;
///计算前k-1 后k-1位置
int fron = 0, bacn = 0;
for(int j = 1; j <= k ; j++)
{
afro[j] = pos[fro].fro;
fro = pos[fro].fro;
if(fro == 0 || fron == k - 1) break;
fron++;
}
for(int j = 1; j <= k; j++)
{
abac[j] = pos[bac].bac;
bac = pos[bac].bac;
if(bac == n + 1 || bacn == k - 1) break;
bacn++;
}
afro[0] = p[i], abac[0] = p[i];
///计算i对答案的贡献
for(int j = fron; j >= 0; j--)
{
if(k - j - 1 > bacn) break;
LL x1=afro[j] - afro[j + 1],x2=abac[k - j ] - abac[k - j -1],x3=i;
ans += x1*x2*x3;
}
}
printf("%lld\n", ans);
}
return 0;
}