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