题意: 给你一个全排列,要你求这个序列的所有区间的第k大的和
思路:一道链表好题。。虽然没用链表做。。这两天有机会用链表补一下。。
拿到题首先分析数据性质,一个是全排列,一个是k比较小,因此我们可以考虑维护每个点周围比这个点向左大0--k-1的数有多少和向右。(相当于枚举这个点作为第K大的区间有多少个),这里注意的是要跳过比枚举点要小的数,如果可以o1跳过,那么单个点维护就是o(k)的复杂度。这里使用了并查集,慢于o1不过也莽过去了。。。
代码:
#include<bits/stdc++.h>
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a))
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define bug puts("bug");
typedef long long ll;
const int maxn=1e6+10;
int t,n,k,x;
ll ans;
int a[maxn],c[maxn],O[maxn],fa[maxn],L[maxn],R[maxn];
int fin(int x){
if(fa[x]==x) return x;
return fa[x]=fin(fa[x]);
}
void update(int x,int y){
int fx=fin(x),fy=fin(y);
fa[fx]=fy;
R[fy]=max(R[fy],R[fx]),L[fy]=min(L[fy],L[fx]);
}
void sl(int x){
vector<ll> lk(k+1,x+1),rk(k+1,x-1);
int lid=x,rid=x;
int mk1=0,mk2=0;
for(int i=0;i<k&&lid>0;i++){
mk1=max(mk1,i);
lid=L[fin(lid)];
if(lid>0&&O[lid]==0&&O[lid-1]) lid=L[fin(lid-1)];
lk[i]=lid;
lid--;
}
for(int i=0;i<k&&rid<=n;i++){
mk2=max(mk2,i);
rid=R[fin(rid)];
if(rid<=n&&O[rid]==0&&O[rid+1]) rid=R[fin(rid+1)];
rk[i]=rid;
rid++;
}
int ll=x+1,rr=x-1;
for(int i=0;i<k;i++){
if(i>mk1||(k-i-1)>mk2) continue;
ll=x+1,rr=x-1;
if(i!=0) ll=lk[i-1];
if(k-i-1!=0)rr=rk[k-i-2];
ans+=(abs(lk[i]-ll))*(abs(rk[k-i-1]-rr))*c[x];
}
}
void modi(int x){
O[x]=1;
if(x-1>0&&O[x-1])update(x,x-1);
if(x+1<=n&&O[x+1])update(x,x+1);
}
int main(){
scanf("%d",&t);
while(t--){
ans=0;
MEM(O,0);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) fa[i]=L[i]=R[i]=i;
for(int i=1;i<=n;i++) scanf("%d",&c[i]),a[c[i]]=i;
for(int i=1;i<=n;i++){
modi(a[i]);
if(i<=n-k+1) sl(a[i]);
}
printf("%lld
",ans);
}
return 0;
}