题意:给你个1-n的排列,找到每一个区间第k大的数,求这些数加起来的总和,如果区间长度小于k,值就为0。
开始一直想用主席树解,但是复杂度太高,不能做,思路卡的时候还是要多转换下思路才行。
解法:找第k大数可以转换为,从小到大枚举x,找一个数x的左边大于x的y个数,右边大于x的k-y-1个数的区间有多少个,然后乘以x然后一起加起来就得到答案了。主要使用双向链表这个神奇的东西,如果前面的数的贡献算完了,那么就可以删除它,因为是从小到大枚举,所以它对后面的区间计算没有贡献。
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <string.h>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#define LL long long
#define INF 0x3f3f3f3f
const int MAX_N = 5e5+10;
const int mod = 100;
const double eps = 1e-6;
using namespace std;
LL a[MAX_N],b[MAX_N];
int pos[MAX_N],pre[MAX_N],nxt[MAX_N];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k,x;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;i++)
{
scanf("%d",&x);
pos[x] = i;
pre[i] = i-1;
nxt[i] = i+1;
}
pre[0] = 0; nxt[n+1] = n+1;
LL ans = 0;
int l,r;
for(int i = 1;i <= n;i++)
{
l = r = 0;
x = pos[i];
for(int j = x;l <= k&&j >= 1;j = pre[j])
a[++l] = j-pre[j];
for(int j = x;r <= k&&j <= n;j = nxt[j])
b[++r] = nxt[j]-j;
LL tmp = 0;
for(int j = 1;j <= l;j++)
if(k-j+1 >=1&&k-j+1 <= r)
tmp+=a[j]*b[k-j+1];
ans+=tmp*i;
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
}
cout<<ans<<endl;
}
return 0;
}
/*
*/