关于此题ABC_392_F - Insert [线段树二分][树状数组倍增]的一些总结

传送门

首先需要用到一个trick:单点正向加入操作变成倒序删除操作。

  • 即我们假定现在有序列1,2,...,n,倒序遍历所给序列,对于当前遍历到的ia[i],我们发现,如果在现有序列(即前面说的1,2,...,n)中找到最后一个为a[i]的位置ji这个数对应答案中的位置就是j,那么我们只需要记录答案,并且[j+1,n]位置的现有序列全部减一即可。这个过程可以用线段树二分实现。

  • 当然也有更简便的方法,我们直接令新序列1,2,...,n位置每个数为1,同样的倒序遍历所给序列变成删除操作,只需要对于每个ia[i],找到新序列中第一个前缀和为a[i]的位置ji这个数对应答案中的位置就是j,然后令新序列中j位置的数为0即可。这可以用树状数组倍增来实现,因为涉及到修改操作,所以不能直接前缀和倍增。代码:

#include<bits/stdc++.h>
    
using namespace std;
    
long long t;
const long long N = 5e5 + 10;
long long n,a[N],tr[N],ans[N];

long long lowbit(long long x) {
    return x & (-x);
}

void add(long long lo,long long v) {
    for(;lo <= n;lo += lowbit(lo))
        tr[lo] += v;
}

long long work(long long k) {
    long long p = 0,s = 0;
    for(long long i = 20;i >= 0;i--) {
        if(p + (1 << i) <= n && s + tr[p + (1 << i)] < k)
            s += tr[p + (1 << i)],p += (1 << i);
    }
    return p + 1;
}
    
void solve() {
    cin >> n;
    for(long long i = 1;i <= n;i++)
        cin >> a[i],add(i,1);
    for(long long i = n;i >= 1;i--) {
        long long pos = work(a[i]);
        ans[pos] = i;
        add(pos,-1);
    }
    for(long long i = 1;i <= n;i++) cout << ans[i] << ' ';
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();
    
    return 0;
}
posted @   孤枕  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示