2019icpc徐州网络赛E.XKC's basketball team(权值线段树)

https://nanti.jisuanke.com/t/41387

题意:给你一个n,m,n个人都有一个Wi权值,问你每个人的右面最远的一个Wj大于等于Wi+m的位置与这个人的位置中间有多少人

(j>i,Wj>=Wi+m);

题解:在每个 i 的右面找大于等于Wi+m的最远位置,用权值线段树倒着维护下标,查询一次,更新一次。偶对了,离散化。

细节:1.倒着做,是因为正着一次全插入后找不到 i 后面比他的大的,会有影响,所以倒着做

           2.写查询是按普通线段树写,查【k,len】(离散化后)内大于Wi+m的,因为是在权值线段树中找大于某个数的

           3.离散化这个一定要注意,如果Wi+m没在离散化数组出现过,    那就是从大于等于该数的下标-len,开始找,是正确的,比如  说1 3 5 7 9 ,找大于4的就是从下标3开始找。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int n,m; 
int a[maxn];
int mx[maxn*4];
vector<int>v;
int getid(int x){
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int l,int r,int p){
	 if(l==r) {
	 	mx[p]=0;
	 	return ;
	 }
	 int mid=(l+r)/2;
	 build(l,mid,p<<1);
	 build(mid+1,r,p<<1|1);
}
void update(int l,int r,int id,int pos,int p){
	if(l==r){
		mx[p]=max(mx[p],pos);
		return ;
	}
	int mid=(l+r)/2;
	if(id<=mid) update(l,mid,id,pos,p<<1);
	else update(mid+1,r,id,pos,p<<1|1);
	mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
int quert(int l,int r,int L,int R,int p){
	if(L<=l&&r<=R){
		return mx[p];
	}
	int mid=(l+r)/2;
	int res=0;
	if(L<=mid) res=max(res,quert(l,mid,L,R,p<<1));
	if(R>mid) res=max(res,quert(mid+1,r,L,R,p<<1|1));
	return res;
}
int ans[maxn];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		v.push_back(a[i]);
	}
	sort(v.begin(),v.end());
	int len=v.size();
	build(1,len,1);
	for(int i=n;i>=1;i--){
		int p=quert(1,len,getid(a[i]+m),len,1);//返回的就是下标 
		//cout<<getid(a[i]+m)<<" "<<p<<endl;
		if(p==0) ans[i]=-1;
		else  ans[i]=p-i-1;
		update(1,len,getid(a[i]),i,1);
	}
	ans[n]=-1;
	for(int i=1;i<=n;i++){
		if(i<n) 
		cout<<ans[i]<<" ";
		else cout<<ans[i]<<endl;
	}
	return 0;
} 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章