现在藏起来一个1到n(2e5)的排列, 对于每个位置i, 给出a[1]到a[i-1]中小于a[i]的数的和s[i], 让我们求这个排列a.
首先我们找找1的位置.
因为没有数比1小, 所以不论1在哪里, 它的s肯定是0;
而如果s中有多个0, 1一定在最后一个0—因为如果1在其他0的位置, 最后一个0没法解释—不论最后一个数是什么, 它肯定比1大, 前面的1一定会对这个数产生贡献, 这样我们就证明了1肯定在最后一个0的位置.
自然我们想到把1到n每个数按照这样的方式一个个归位.
一开始我的思路是这样的:
1的位置已经确定, 2的位置要么在1前面, 要么在1后面. 如果在1前, 那么它一定是1前最后一个0; 如果在1后, 它一定是1后面最后一个1. 然后一个个确定.
但是这样是不行的. 且不说程序难以实现, 单是判断在前在后就无从下手.
换一个思路(zl666):
每当将一个数归位, 我们就将它产生的贡献去除, 这样我们每次只须去数组中找最后一个0就好.
具体地, 找到1的位置pos, s数组从pos开始后面全减1; 最后一个0就是2的位置pos2, s从pos2后面全减2; 最后一个0就是3的位置pos3, s从pos3后面全减3······
我们可能要问, 如果2在1前, 那么1的的位置会不会再被用到? 所以我们要在确定1的位置后把1的位置剔除考虑范围, 具体操作其实可以把s[1]赋为INF, 保证它在所有可能的-2/-3/-4/···中不会减到0就好了.
那么这个题就转化为求s数组最后一个0的位置就是i的位置, 然后把它改成INF, 它后面所有的数-i, 再找最后一个0的位置···
这个过程可以用线段树解决:
维护区间最小值, 如果右区间最小值是0, 那么往右走; 否则往左走. 这样就能log(n)找到最后一个0的位置.
改成INF就是单点更新, pos+1到n减i就是区间更新. lazy维护减了多少.
代码:
ll n, a[M];
struct node {
ll lz, mx;
} tree[M * 4];
void build(int pos, int l, int r) {
if (l == r) {
tree[pos].mx = tree[pos].lz = read();
return;
}
int mid = (l + r) / 2;
build(pos * 2, l, mid);
build(pos * 2 + 1, mid + 1, r);
tree[pos].mx = sml(tree[pos * 2].mx, tree[pos * 2 + 1].mx);
}
void pushDown(int pos) {
tree[pos * 2].lz += tree[pos].lz;
tree[pos * 2 + 1].lz += tree[pos].lz;
tree[pos * 2].mx += tree[pos].lz;
tree[pos * 2 + 1].mx += tree[pos].lz;
tree[pos].lz = 0;
}
void pushUp(int pos) {
tree[pos].mx = sml(tree[pos * 2].mx, tree[pos * 2 + 1].mx);
}
int query(int pos, int l, int r) {
if (l == r)return l;
pushDown(pos);
int mid = (l + r) / 2;
if (tree[pos * 2 + 1].mx == 0)query(pos * 2 + 1, mid + 1, r);
else query(pos * 2, l, mid);
}
void update1(int pos, int l, int r, int p) {
if (l == r && l == p) {
tree[pos].mx = INF;
return;
}
pushDown(pos);
int mid = (l + r) / 2;
if (p <= mid)update1(pos * 2, l, mid, p);
else update1(pos * 2 + 1, mid + 1, r, p);
pushUp(pos);
}
void update2(int pos, int l, int r, int ul, int ur, int v) {
if (ul <= l && ur >= r) {
tree[pos].mx -= v;
tree[pos].lz -= v;
return;
}
pushDown(pos);
int mid = (l + r) / 2;
if (ul <= mid)update2(pos * 2, l, mid, ul, ur, v);
if (ur > mid)update2(pos * 2 + 1, mid + 1, r, ul, ur, v);
pushUp(pos);
}
void init() {
n = read();
build(1, 1, n);
for (int i = 1; i <= n; ++i) {
int pos = query(1, 1, n);
a[pos] = i;
update1(1, 1, n, pos);
update2(1, 1, n, pos, n, i);
}
for (int i = 1; i <= n; ++i)write(a[i]), space;
enter;
}