Atcoder ABC392F Insert 题解 [ 绿 ] [ 线段树二分 ] [ 倒序操作 ]

Insert:绷不住了,ds 学傻了导致一直在写一个假的没边的做法,赛后 5min 胡出正解,唐。

思路

首先正着操作想想就觉得不太好搞,所以考虑一个经典 trick:单点正向加入操作转化为倒序删除操作。

于是插入就变成了删除,那么继续考虑删除一个数意味着啥,意味着最终后面的数在当前的操作下位置全部减 1 了。那么这个删除的数要不要管呢?显然是不要的,我们查询的时候可以查询最后一个值为 x 的下标,这样就可以自动忽略删除了的数。

这个过程可以用线段树实现,维护最终答案下每个位置对应当前操作下的位置下标,每次在线段树上二分得到最后一个值为 x 的下标 k 后,对区间 [k+1,n] 进行减 1 操作即可。

时间复杂度 O(nlogn)

后来发现这个东西可以直接权值线段树维护每个位置是否删除,然后查询排名为 p 的点即可,不过我只写了上面那个普通线段树的做法。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
using pl=pair<ll,int>;
const int N=500005;
int n,p[N],a[N],ans[N];
struct Node{
    int l,r;
    int mn,tag=0;
};
struct Segtree{
    Node tr[4*N];
    void pushup(int p)
    {
        tr[p].mn=min(tr[lc].mn,tr[rc].mn);
    }
    void pushdown(int p)
    {
        if(tr[p].tag)
        {
            tr[lc].tag+=tr[p].tag;
            tr[rc].tag+=tr[p].tag;
            tr[lc].mn+=tr[p].tag;
            tr[rc].mn+=tr[p].tag;
        }
        tr[p].tag=0;
    }
    void build(int p,int ln,int rn)
    {
        tr[p]={ln,rn,a[ln],0};
        if(ln==rn)return;
        int mid=(ln+rn)>>1;
        build(lc,ln,mid);
        build(rc,mid+1,rn);
        pushup(p);
    }
    void update(int p,int ln,int rn)
    {
        if(ln<=tr[p].l&&tr[p].r<=rn){tr[p].tag-=1;tr[p].mn-=1;return;}
        pushdown(p);
        int mid=(tr[p].l+tr[p].r)>>1;
        if(ln<=mid)update(lc,ln,rn);
        if(rn>=mid)update(rc,ln,rn);
        pushup(p);
    }
    int query(int p,int x)
    {
        if(tr[p].l==tr[p].r)return tr[p].l;
        pushdown(p);
        int mid=(tr[p].l+tr[p].r)>>1;
        if(tr[rc].mn<=x)return query(rc,x);
        return query(lc,x);
    }
}tr1;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>p[i];
    for(int i=1;i<=n;i++)a[i]=i;
    tr1.build(1,1,n);
    for(int i=n;i>=1;i--)
    {
        int x=tr1.query(1,p[i]);ans[x]=i;
        if(x+1<=n)tr1.update(1,x+1,n);
    }
    for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
    return 0;
}
posted @   KS_Fszha  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示