D. Restore Permutation
题意:给定n个数a[i],a[ i ]表示在【b[1],b[i-1]】这些数中比 b[i]小的数的和,要你构造这样的b[i]序列
题解:利用树状数组 求比b[i]小的数的和,在从大到小二分枚举最大的一个数x,使得左边小于x的所有数的和小于等于a[i],vis保存记录x即可
#include<iostream> #include<string.h> #define ll long long using namespace std; ll a[200005],b[200005],c[200005],vis[200005]; //a[i]保存原始数据,b[i]保存比a[i]小的数的个数,c[i]保存所有比a[i]小的数的和 ll lowbit(ll x) { return x&(-x); } ll getsum(ll x)//求比x小的数的和 { ll ans=0; while(x>0) { ans=ans+c[x]; x=x-lowbit(x); } return ans; } void add(ll x,ll y)//更新,对第x个位置的数进行更新,y是更新值 { while(x<=200000) { b[x]=b[x]+1; c[x]=c[x]+y; x=x+lowbit(x); } } int main() { int n; while(~scanf("%d",&n)) { ll ans=0,sum=0; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); add(i,i);//将第x个位置的值,修改为x } for(int i=n;i>=1;i--) { ll le=1,ri=n,mid; while(le<ri)//从右往左,二分枚举最大的x,使得左边小于x的所有数的和小于等于a[i] { mid=(le+ri+1)/2; if(getsum(mid-1)<=a[i]) le=mid; else ri=mid-1; } vis[i]=le; add(le,-le);//枚举到这个数之后就把这个数置为零 } for(int i=1;i<=n;i++) { if(i==1) printf("%lld",vis[i]); else printf(" %lld",vis[i]); } printf(" "); } return 0; }