2019icpc南京网络赛 F 主席树

题意

给一个\(n\)的全排列数组\(a\),求一个递推数组每一项的值:\(ans[i]=ans[j]+1\)\(j\)\(a[pos[i]-k]到a[pos[i]+k],(pos[i]为i在数组a中的下标)\)中小于\(i\)的最大的值。

分析

这题set的做法更优秀,但是想练习一下在主席树上二分。

按权值建主席树,对每个\(i\)去查询\(a[pos[i]-k]到a[pos[i]+k]\)中小于\(i\)的最大值,查询时先查询右儿子,再查询左儿子,因为找到的右儿子一定比左儿子大,直到找到一个满足条件的点。

Code

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define ll long long
using namespace std;
const int inf=1e9;
const int mod=1e9+7;
const int maxn=1e5+10;
int T;
int n,k;
int a[maxn],b[maxn],ans[maxn],tr[maxn*30],ls[maxn*30],rs[maxn*30],rt[maxn],tot;
void bd(int l,int r,int &p){
	tr[++tot]=tr[p],ls[tot]=ls[p],rs[tot]=rs[p],p=tot;
	if(l==r) return;
	int mid=l+r>>1;
	bd(l,mid,ls[p]);bd(mid+1,r,rs[p]);
}
void up(int k,int l,int r,int &p){
	tr[++tot]=tr[p]+1,ls[tot]=ls[p],rs[tot]=rs[p],p=tot;
	if(l==r) return;
	int mid=l+r>>1;	
	if(k<=mid) up(k,l,mid,ls[p]);
	else up(k,mid+1,r,rs[p]);
}
int qy(int dl,int dr,int l,int r,int a,int b){
	if(tr[b]-tr[a]==0) return 0;
	if(l>=dl&&r<=dr){
		if(l==r) return l;
		int mid=l+r>>1,res=0;
		if(dr>mid) res=qy(dl,dr,mid+1,r,rs[a],rs[b]);
		if(dl<=mid&&res==0) res=qy(dl,dr,l,mid,ls[a],ls[b]);
		return res;
	}int mid=l+r>>1,res=0;
	if(dr>mid) res=qy(dl,dr,mid+1,r,rs[a],rs[b]);
	if(dl<=mid&&res==0) res=qy(dl,dr,l,mid,ls[a],ls[b]);
	return res;
}
int main(){
	//ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	scanf("%d",&T);
	while(T--){
		tot=0;
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			b[a[i]]=i;
		}
		bd(1,n,rt[0]);
		for(int i=1;i<=n;i++){
			rt[i]=rt[i-1];
			up(a[i],1,n,rt[i]);
		}ans[1]=1;
		for(int i=2;i<=n;i++){
			int l=max(0,b[i]-k-1),r=min(n,b[i]+k);
			ans[i]=ans[qy(1,i-1,1,n,rt[l],rt[r])]+1;
		}
		for(int i=1;i<=n;i++){
			printf("%d",ans[i]);
			if(i==n) printf("\n");
			else printf(" ");
			ans[i]=0;
		}
	}
	return 0;
}
posted @ 2019-09-01 22:15  xyq0220  阅读(208)  评论(0编辑  收藏  举报