题目链接:https://www.luogu.org/problem/CF1208D
题意:现在有一个从1到n的一个全排列,但是你不知道这个排列到底是什么,但是你有一个sum[i],sum[i]的值是所有满足j<i并且a[j]<a[i]的值之和,给出每个点的sum[i],求出原本的全排列
分析:我们从最后一个点n开始看,sum[n]肯定表示的是一段连续的都比a[n]小的的从1到j的数的和,根据这个和我们可以找出a[n]
而sum[n-1]表示的是一段连续的都比a[n-1]小的的从1到k的数的和(这里注意,如果本身a[n]也比a[n-1]小的话,这个和里是不包含a[n]的,也就是说事实上此时的sum[n-1]表示的是[1,a[n]),(a[n],k)的和
那么思路就出来了,我们可以从后往前一个个确定,确定了一个数之后就把那个数赋为0 ,之后找和的时候就不会遇到那个数了
也就是说会用单单点修改,和查询时哪个区间的和 线段树即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=1e18; const int maxn=2e5+7; const int mod=1e9+7; #define mem0(a) memset(a,0,sizeof(a)) #define meminf(a) memset(a,0x3f,sizeof(a)) #define ls rt<<1 #define rs rt<<1|1 ll a[maxn],ans[maxn]; ll dat[maxn<<2]; void add(int rt,int l,int r,int x,int y){ if(l==r){ dat[rt]+=y; return ; } ll mid=(l+r)>>1; if(x<=mid)add(ls,l,mid,x,y); else add(rs,mid+1,r,x,y); dat[rt]=dat[ls]+dat[rs]; } ll query(int rt,int l,int r,ll v){ if(l==r){ return l; } ll mid=(l+r)>>1; if(dat[ls]>v)return query(ls,l,mid,v); else return query(rs,mid+1,r,v-dat[ls]); } int main(){ int n;scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%I64d",&a[i]); add(1,1,n,i,i); } for(int i=n;i>0;i--){ ans[i]=query(1,1,n,a[i]); add(1,1,n,ans[i],-ans[i]); //将第i个数赋为0,之后就不会加了 } for(int i=1;i<n;i++)printf("%I64d ",ans[i]); printf("%I64d ",ans[n]); return 0; }