题意:有一个隐藏的序列,序列值为1-N,给定一个序列,序列中每个值为隐藏序列中前面小于该点值的所有值之和,求出这个隐藏的序列
思路:显然给定序列中值为0的点对应的肯定为隐藏序列中值为1的点,从1到N枚举i,每次找出给定最后一个值为0的点,将其删除,隐藏序列中该点值即为i,位于改点右边的值减去i,继续找下一个最后值为0的点继续该操作,最后给定序列肯定值全为0,也即找到了答案序列。为了找最后一个值为0的点,维护一个线段树储存区间最小值,查找时先从右子树开始找,若某点值为0,返回该点坐标,这样找到的就是最后一个值为0的点。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4e6+5;
const int inf = 0x3f3f3f3f;
ll n, tree[maxn], a[maxn], res[maxn], lz[maxn];
void pushdown(ll p)
{
if (lz[p]) {
tree[p<<1] += lz[p];
tree[p<<1|1] += lz[p];
lz[p<<1] += lz[p];
lz[p<<1|1] += lz[p];
lz[p] = 0;
}
}
void build(ll p, ll l, ll r)
{
if (l == r) {
tree[p] = a[l];
return;
}
ll mid = (l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
tree[p] = min(tree[p<<1], tree[p<<1|1]);
}
void update(ll k, ll x, ll y, ll l, ll r, ll p)
{
if (r < x || l > y)
return;
if (x <= l && y >= r) {
lz[p] += k;
tree[p] += k;
return;
}
pushdown(p);
ll mid = (l+r)>>1;
update(k, x, y, l, mid, p<<1);
update(k, x, y, mid+1, r, p<<1|1);
tree[p] = min(tree[p<<1], tree[p<<1|1]);
}
ll query(ll p, ll l, ll r)
{
if (l == r)
return l;
pushdown(p);
ll mid = (l+r)>>1, ans = -1;
if (tree[p<<1|1] == 0)
ans = query(p<<1|1, mid+1, r);
else
ans = query(p<<1, l, mid);
tree[p] = min(tree[p<<1], tree[p<<1|1]);
return ans;
}
int main()
{
scanf("%lld", &n);
memset(lz, 0, sizeof(lz));
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, 1, n);
for (int i = 1; i <= n; i++) {
ll k = query(1, 1, n);
res[k] = i;
update(2e10, k, k, 1, n, 1);
update(-i, k+1, n, 1, n, 1);
}
for (int i = 1; i <= n; i++)
printf("%lld ", res[i]);
printf("\n");
}