关于此题ABC_392_F - Insert [线段树二分][树状数组倍增]的一些总结
首先需要用到一个trick:单点正向加入操作变成倒序删除操作。
-
即我们假定现在有序列
,倒序遍历所给序列,对于当前遍历到的 和 ,我们发现,如果在现有序列(即前面说的 )中找到最后一个为a[i]的位置 , 这个数对应答案中的位置就是 ,那么我们只需要记录答案,并且令 位置的现有序列全部减一即可。这个过程可以用线段树二分实现。 -
当然也有更简便的方法,我们直接令新序列
位置每个数为1,同样的倒序遍历所给序列变成删除操作,只需要对于每个 和 ,找到新序列中第一个前缀和为 的位置 , 这个数对应答案中的位置就是 ,然后令新序列中 位置的数为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;
}