http://codeforces.com/problemset/problem/1208/D
题意:要求一个序列,满足1-n之间的数每个出现一次。
给出该数之前比它小的数之和,求该序列是什么?
思路:方法一:从后往前找,找到第一个为0的位置,就能确定该位置为1,从该位置往后的 和数列 都减去1,
再次从后往前找0,确定该位置为2,其后面的和数列都减去2,
直到所有位置都被确定。
方法二:从后往前依次确定每个位置的值,由和数列就能知道该数是由前k个比它小的没有用过的数构成。
这个数就是第k+1个小的没有用过的数。
建线段树,维护每个数值,及它们的和,确定后再将该数变为0,因为前面的和将不再包含这个数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=2e5+5;
ll t[maxn<<2],n,s[maxn],ans[maxn];
void add(ll k,ll l,ll r,ll x,ll y)
{
if(l==r){
t[k]+=y;return ;
}
ll mid=(l+r)>>1;
if(x<=mid) add(k<<1,l,mid,x,y);
else add(k<<1|1,mid+1,r,x,y);
t[k]=t[k<<1]+t[k<<1|1];
}
ll query(ll k,ll l,ll r,ll v)
{
if(l==r){
return l;
}
ll mid=(l+r)>>1;
if(t[k<<1]>v) return query(k<<1,l,mid,v);
else return query(k<<1|1,mid+1,r,v-t[k<<1]);
}
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld",&s[i]);
add(1,1,n,i,i);
}
for(ll i=n;i>=1;i--){
ans[i]=query(1,1,n,s[i]);
add(1,1,n,ans[i],-ans[i]);
}
for(ll i=1;i<=n;i++){
if(i<n) printf("%lld ",ans[i]);
else printf("%lld\n",ans[i]);
}
return 0;
}