Codeforces 1208D Restore Permutation

题目链接:https://www.luogu.org/problem/CF1208D

题意:现在有一个从1n的一个全排列,但是你不知道这个排列到底是什么,但是你有一个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;
}
原文地址:https://www.cnblogs.com/qingjiuling/p/11624286.html