CF91B Queue
题意翻译
-
给定 nnn 个正整数 a1…na_{1 dots n}a1…n。
-
需要输出一行 nnn 个数,设此时正在处理第 iii 个数:
- 设 aj<aia_j<a_iaj<ai 且 j>ij >ij>i。
- 在满足第一条的基础上使 j−i−1j-i-1j−i−1 尽可能大,此时 j−i−1j-i-1j−i−1 即为答案。
-
2≤n≤105,ai≤1092 leq n leq 10^5,a_i leq 10^92≤n≤105,ai≤109,如果对于某个 iii,没有任何一个 jjj 满足 aj<aia_j<a_iaj<ai 且 j>ij>ij>i,输出
-1
。
题解:
首先概括题意:要求一个序列每一个位置最右侧的比自己小的数是谁。没有输出-1.
发现完全可以用线段树处理,维护一个区间最小值的线段树,查询的时候返回位置,每次优先查询右面。
有人说这叫线段树上二分,我觉得线段树本身就是二分的一个结构,所以不太算吧。
那么就直接上代码吧:
#include<cstdio>
#include<algorithm>
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=1e5+5;
int n;
int a[maxn];
int mn[maxn<<2];
void build(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
{
mn[pos]=a[l];
return;
}
build(lson,l,mid);
build(rson,mid+1,r);
mn[pos]=min(mn[lson],mn[rson]);
}
int query(int pos,int l,int r,int x,int y,int k)//查询x-y区间中下标最大的小于k的数的下标
{
int mid=(l+r)>>1;
int ret=0;
if(mn[pos]>=k)
return -1;
if(l==r)
return l;
if(y>mid)
{
ret=query(rson,mid+1,r,x,y,k);
if(ret!=-1)
return ret;
}
if(x<=mid)
ret=query(lson,l,mid,x,y,k);
return ret;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
for(int i=1;i<n;i++)
{
int ans=query(1,1,n,i+1,n,a[i]);
if(ans==-1)
printf("%d ",ans);
else
printf("%d ",ans-i-1);
}
printf("-1");
return 0;
}