NOIP模拟 排列【权值线段树】

来源:互联网 发布:网络人软件 编辑:程序博客网 时间:2024/05/22 11:38

题目大意:

对于一个1到n的排列,若知道每一位的逆序数(第i位a[i]的逆序数就是a[1]~a[i-1]中比a[i]大的数的个数),则能求出原排列。
现在对于排列{a[i]},给出{p[i]}。p[i]表示a[1]~a[i]的逆序数和。请你求出原排列。(1<=n<=100000)

解题思路:

先求出每个数的逆序数,仍设为p[i],倒着确定,那对于最后一个未确定的数a[i]来说,它前面有p[i]个比它大的数,所以他就是剩下未确定的中的第i-p[i]小的数,用权值线段树维护即可。

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<vector>#include<queue>#define ll long longusing namespace std;int getint(){    int i=0,f=1;char c;    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());    if(c=='-')f=-1,c=getchar();    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';    return i*f;}ll getll(){    ll i=0,f=1;char c;    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());    if(c=='-')f=-1,c=getchar();    for(;c>='0'&&c<='9';c=getchar())i=i*10+c-'0';    return i*f;}const int N=100005;int n,a[N],tr[N<<2];ll p[N];void build(int k,int l,int r){    if(l==r)    {        tr[k]=1;        return;    }    int mid=l+r>>1;    build(k<<1,l,mid),build(k<<1|1,mid+1,r);    tr[k]=tr[k<<1]+tr[k<<1|1];}int query(int k,int l,int r,int num){    if(l==r)    {        tr[k]=0;        return l;    }    int mid=l+r>>1,res;    if(num<=tr[k<<1])res=query(k<<1,l,mid,num);    else res=query(k<<1|1,mid+1,r,num-tr[k<<1]);    tr[k]=tr[k<<1]+tr[k<<1|1];    return res;}int main(){    //freopen("premu.in","r",stdin);    //freopen("premu.out","w",stdout);    n=getint();    for(int i=1;i<=n;i++)p[i]=getll();    for(int i=n;i;i--)p[i]-=p[i-1];    for(int i=1;i<=n;i++)p[i]=i-p[i];    build(1,1,n);    for(int i=n;i;i--)        a[i]=query(1,1,n,p[i]);    for(int i=1;i<=n;i++)        cout<<a[i]<<' ';    return 0;}
原创粉丝点击