解法一 树状数组:
```math
倒序,对于每个s_{i},找出从1-n中的未被利用且和为s_{i}的前缀和,则p_{i}为这些数中最大的数+1,每次找都后需要及时删去
此处查找使用倍增的方法。
数组数组+倍增,单次操作O(log n)
树状数组+二分,单次操作O(log n*log n)
#include <bits/stdc++.h> using namespace std; #define ll long long ll s[200050], c[200050]; ll p[25]; int n, t, h[200050]; void add(int x, int t) { while (x <= n) { c[x] -= t; x += x&-x; } } int main() { p[0] = 1; for (int i = 1; i<22; i++)p[i] = p[i - 1] << 1; scanf("%d",&n); t = log(n) / log(2); for (int i = 1; i <= n; i++) { scanf("%lld",&s[i]); c[i] += i; if (i + (i&-i) <= n)c[i + (i&-i)] += c[i]; } // for(int i=1;i<=n;i++) // cout<<c[i]<<" "; // cout<<endl; for (int i = n; i; i--) { int ans = 0; ll sum = 0; for (int j = t; j >= 0; j--) { if (ans + p[j] <= n && sum + c[ans + p[j]] <= s[i] ) { sum += c[ans + p[j]]; ans += p[j]; } } h[i] = ans+1; add(ans+1, ans+1); } for (int i = 1; i <= n; i++) printf("%d ", h[i]); puts(""); return 0; }
解法二:线段树
#include<bits/stdc++.h> #pragma GCC optimize(3) #define max(a,b) a>b?a:b using namespace std; typedef long long ll; const int N=2e5+5; ll tr[N<<2]; ll s[N]; int pos[N]; void build(int l,int r,int p){ if(l==r){ tr[p]=l; return ; } int mid=(l+r)>>1; build(l,mid,p<<1); build(mid+1,r,p<<1|1); tr[p]=tr[p<<1]+tr[p<<1|1]; } int query(int l,int r,int p,ll val){ if(l==r){ tr[p]=0; return l; } int mid=(l+r)>>1; int ans=0; if(tr[p<<1]>val){ ans=query(l,mid,p<<1,val); tr[p]-=ans; } else{ ans=query(mid+1,r,p<<1|1,val-tr[p<<1]); tr[p]-=ans; } return ans; } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%I64d",&s[i]); build(1,n,1); for(int i=n;i>=1;i--){ pos[i]=query(1,n,1,s[i]); } for(int i=1;i<=n;i++) printf("%d%c",pos[i]," \n"[i==n]); return 0; }
此题与POJ 2182类似。