题目
给出n个数的序列,求出每一个数右边最后一个比他小的数的位置差。
题解
考虑单调队列做法
维护一个单调递减的队列。
对于一个数来说 最优被定义为 尽可能小,同时位置要小。
于是对于一个新入队的数来说,如果他比最小的数大的话。其位置也大于最小的数。则这个数是无用的。
于是对于每个新入队的数,在入队前在单调队列中二分查找一下最后一个比他的小的数的位置。
如果满足单调性则入队。否则直接不考虑。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n,arr[maxn],ans[maxn];
typedef pair<int,int> pint;
pint qu[maxn],tmp;
int main()
{
while(~scanf("%d",&n)) {
for(int i=n;i>=1;i--) scanf("%d",&arr[i]);
for(int i=1,h=1,t=0;i<=n;i++) {
tmp = make_pair(i,arr[i]);
if(h > t || (h <= t && qu[t].second >= arr[i])) {
ans[i] = -1;
qu[++t] = tmp;
}
else {
int l = h,r = t;
while(l <= r) {
int mid = (l+r)>>1;
if(qu[mid].second < arr[i]) r = mid - 1;
else l = mid + 1;
}
ans[i] = i - qu[l].first - 1;
}
}
for(int i=n;i>=2;i--) printf("%d ",ans[i]);
printf("%d\n",ans[1]);
}
return 0;
}
线段数做法
线段树维护区间最小值。
初始化无穷INF
每次递归查询离当前位置最远的小于该数的位置。
之后更新该点的值。
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;
struct SegTree {
int Min[maxn<<2];
void push_up(int rt) {
Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
void build(int l,int r,int rt) {
if(l == r) {
Min[rt] = INF;
return;
}
int mid = (l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int pos,int val,int l,int r,int rt) {
if(l == r) {
Min[rt] = val;
return ;
}
int mid = (l+r)>>1;
if(pos <= mid) update(pos,val,l,mid,rt<<1);
else update(pos,val,mid+1,r,rt<<1|1);
push_up(rt);
}
int query(int val,int l,int r,int rt) {
if(l == r) return l;
int mid = (l+r)>>1;
if(Min[rt<<1] < val) return query(val,l,mid,rt<<1);
if(Min[rt<<1|1] < val) return query(val,mid+1,r,rt<<1|1);
}
}seg;
int n,arr[maxn],ans[maxn];
int main()
{
while(~scanf("%d",&n)) {
seg.build(1,n,1);
for(int i=n;i>=1;i--) scanf("%d",&arr[i]);
for(int i=1;i<=n;i++) {
if(seg.Min[1] >= arr[i]) {
ans[i] = -1;
}
else {
ans[i] = i - seg.query(arr[i],1,n,1) - 1;
}
seg.update(i,arr[i],1,n,1);
}
for(int i=n;i>1;i--) printf("%d ",ans[i]);
printf("%d\n",ans[1]);
}
return 0;
}