思路:
从小枚举i,当i作为序列的第一个的时候,记i在原数组中的下标为idx,在[idx-k,idx+k](边界需要判断下)的范围找到比i小的数中最大的那个记为maxn,那么i的答案就是maxn的答案+1,如果没有maxn,那么答案就是1。查找maxn我是直接使用暴力主席树找小于等于i的个数k,然后查找区间第k-1的数,跑了近4000ms,卡过。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int root[N],tot;
int lson[N*25],rson[N*25],sz[N*25];
void update(int last,int cur,int l,int r,int k){
lson[cur]=lson[last];
rson[cur]=rson[last];
sz[cur]=sz[last]+1;
if(l==r)return;
int mid=(l+r)>>1;
if(k<=mid)update(lson[last],lson[cur]=++tot,l,mid,k);
else update(rson[last],rson[cur]=++tot,mid+1,r,k);
}
int query(int last,int cur,int l,int r,int k){//查找小于等于k的数量
if(k<l)return 0;
if(k>=r){
return sz[cur]-sz[last];
}
int mid=(l+r)>>1;
int sum=0;
sum+=query(lson[last],lson[cur],l,mid,k);
sum+=query(rson[last],rson[cur],mid+1,r,k);
return sum;
}
int query2(int last,int cur,int L,int R,int k)//区间第k小
{
// printf("%d %d %d\n",L,R,k);
if(L==R)return L;
int mid=(L+R)>>1;
int lll=sz[lson[cur]]-sz[lson[last]];
// printf("sz:%d\n",lll);
if(lll>=k)return query2(lson[last],lson[cur],L,mid,k);
else return query2(rson[last],rson[cur],mid+1,R,k-lll);
}
int ans[N];
int pos[N];
int main(){
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++){
int n,k;
scanf("%d%d",&n,&k);
tot=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[a[i]]=i;
update(root[i-1],root[i]=++tot,1,n,a[i]);
}
for(int i=1;i<=n;i++){
int l=max(1,pos[i]-k);
int r=min(n,pos[i]+k);
int ss=query(root[l-1],root[r],1,n,i);
if(ss==1){
ans[i]=1;
}
else{
int p=query2(root[l-1],root[r],1,n,ss-1);
ans[i]=ans[p]+1;
}
}
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n?'\n':' ');
}
}
return 0;
}