Kanade's sum
这个题目主要的思路是对的
就是计算每个数字出现的次数
然后我发现我们可以对每个数字,往左找k个比他大的,往右找k个比他大的
然后他们的位置之差乘起来就是出现的次数
等下会补个图详细解释下
然而如果我们找k个比他大的话,线性找的话,时间复杂度会爆炸
所以我们就想从最小的开始找,然后找一个就把他从数组中删去
那么对于下一个数,他的左右k个就是k个比他大的数了
所以我们要保存每个数的位置
接下来的关键就是把一个数从数组中删去了
我们删去后,数组中他后面的所有数在数组中的位置都会改变
那么我们有要花费n的时间去修改位置,这是不划算的
现在题解就有一种方法,直接数组模拟链表
模拟++和--,真的是非常巧妙
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
#define maxn 600000
using namespace std;
int n,kk;
int a[maxn],pre[maxn],ne[maxn],poi[maxn];//pre[i]表示第i位的前面一位的位置
ll cal(int k)
{
int pos=poi[k];//先找到k在数组中的位置
int q[100],h[100],nq(0),nh(0);
for (int i=pre[pos];i!=-1;i=pre[i])//往前找k个比它小的
{
nq++;q[nq]=poi[a[i]];
if (nq==kk) break;
}
if (nq<kk) q[++nq]=0;//如果不够k个就加上一个位置0,这个为什么画图就知道了,开始没注意,wa了好久
for (int i=ne[pos];i!=-1;i=ne[i])//同理
{
nh++;h[nh]=poi[a[i]];
if (nh==kk) break;
}
if (nh<kk) h[++nh]=n+1;
q[0]=pos;h[0]=pos;
ll ans(0);
for (int i=1;i<=nq;i++)//开始算
{
if (kk-i+1<=nh)
ans+=(ll)(q[i-1]-q[i])*(ll)(h[kk-i+1]-h[kk-i]);
}
int pp=pre[pos];//删去pos位
int nn=ne[pos];
if(pre[pos]!=-1)ne[pre[pos]]=nn;
if(ne[pos]!=-1)pre[ne[pos]]=pp;
pre[pos]=ne[pos]=0;
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&kk);
memset(pre,-1,sizeof(pre));
memset(ne,-1,sizeof(ne));
for (int k=1;k<=n;k++)
{
scanf("%d",&a[k]);
poi[a[k]]=k;
pre[k]=k-1;ne[k]=k+1;
}
pre[1]=-1;ne[n]=-1;
ll ans(0);
for (int k=1;k<=n-kk+1;k++)//从小到大计算
{
ans+=(ll)k*cal(k);
//cout<<zzz<<endl;
}
printf("%lld\n",ans);
}
return 0;
}