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;
}