D. Restore Permutation 树状数组+二分

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("\n");
    }
    return 0;
}

 

posted @ 2019-09-24 21:16  知道了呀~  阅读(226)  评论(0编辑  收藏  举报