首先有一个关系:当一个数是第k大的时候,前面有x个比它大的数,那么后面就有k-x-1个比它大的数。然后我们对下标从1开始计算,换算一下,就是程序中的样子。
比赛的时候队友想出了用set来维护。一开始是一个空的set,先插入大的数,那么当之后插入数的时候,他们之间的pos距离就代表它有多少个小于它的,然后根据上面的关系,对于每个数最多使得迭代器跳k次,就可以快速维护了。其实想法和正解差不多,但是因为其迭代器使用不熟练,而且我还死磕自己错误的想法。
题解的思路其实差不多,一开始先维护一个满的链表,然后从小到大删除,每次算完一个数,就在链表里面删除,算x的时候,保证删除的数都比x小,都可以用来算贡献。i和pre[i]和nxt[i]的距离就是小于当前的数的数目+1。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll maxn=5e5+5;
int a[maxn],pre[maxn],nxt[maxn],pos[maxn];
int main(int argc, char const *argv[])
{
int T,n,k;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=i-1,nxt[i]=i+1,pos[a[i]]=i;;
}
pre[0]=0,nxt[n]=n+1;
ll sum=0;
for(int j=1;j<=n;j++){
int x=pos[j];
int rq[110];
int lc,rc;
lc=rc=0;
for(int i=x;i<=n&&rc<k;i=nxt[i]){
rq[++rc]=nxt[i]-i;
}
ll ans=0;
for(int i=x;i>0&&lc<k;i=pre[i]){
lc++;
if(k-lc+1>rc) continue;
else ans+=(i-pre[i])*rq[k-lc+1];
}
sum+=ans*j;
pre[nxt[x]]=pre[x];
nxt[pre[x]]=nxt[x];
}
printf("%lld\n",sum);
}
return 0;
}