【题意】给一个序列,从右边开始看,对于第i个数字a[i],在右边找到一个比它小(严格小)的,最靠右的位置,k,输出k-i-1,如果一个都找不到,输出-1。对于序列的每个元素都要输出。

【解题方法1】 线段树。节点保存最小值,每次把节点更新为INF,然后查找整个区间最右边比它小的数的位置。

【代码1】


int n, a[maxn];
struct node{
    int l, r, v;
}T[maxn*4];
void pushup(int rt)
{
    T[rt].v = min(T[rt*2].v, T[rt*2+1].v);
}
void Build(int l, int r, int rt)
{
    T[rt].l = l, T[rt].r = r;
    if(l == r){
        T[rt].v = a[l];
        return;
    }
    int mid = (l + r) / 2;
    Build(l, mid, rt * 2);
    Build(mid + 1, r, rt * 2 + 1);
    pushup(rt);
}
void update(int pos, int rt){
    if(T[rt].l == T[rt].r){
        T[rt].v = INF;
        return ;
    }
    int mid = (T[rt].l + T[rt].r) / 2;
    if(pos <= mid) update(pos, rt * 2);
    else update(pos, rt * 2 + 1);
    pushup(rt);
}
int queryans(int x, int rt){
    if(T[rt].l == T[rt].r){
        return T[rt].l;
    }
    if(T[rt * 2 + 1].v < x) return queryans(x, rt * 2 + 1);
    else return queryans(x, rt * 2);
}
int main()
{
    cin >> n;
    REP2(i, 1, n) cin >> a[i];
    Build(1, n, 1);
    REP2(i, 1, n){
        update(i, 1);
        if(T[1].v >= a[i]) cout << "-1" << " ";
        else cout << queryans(a[i], 1) - i - 1 << " ";
    }
    cout << endl;
}


【解题方法2】 单调队列。我们需要维护一个序列,这个序列保存着已扫描的值里面的最小值,同时这个序列具有单调性,从开始到当前,序列需要保持递减,对于插入的时候如果无法保持序列的单调性的话,就不要需要插入的值,否则就插入。在求结果的时候,我们可以二分求出比考察点小的,里考察点最远(最右)的那个点,然后求出答案即可。


int n, a[maxn], ans[maxn];
vector <int> v, pos;
int main()
{
    cin >> n;
    REP1(i, 0, n) cin >> a[i];
    REP3(i, n - 1, 0){
        if(v.size() == 0 || v.back() >= a[i]){
            v.push_back(a[i]);
            pos.push_back(i);
            ans[i] = -1;
        }
        else{
            int j = lower_bound(v.rbegin(), v.rend(), a[i]) - v.rbegin();
            j =(int) v.size() - j;
            ans[i] = pos[j] - i - 1;
        }
    }
    REP1(i, 0, n) cout << ans[i] << " ";
}