[NOIP2017模拟]排列

来源:互联网 发布:qq拉圈圈软件 编辑:程序博客网 时间:2024/06/05 16:35

2017.11.3 T2 2033

样例数据
输入

3
0 1 2

输出

3 1 2

分析:思想是这样,先把每个数的逆序对数求出来,从最后一个数开始(这个数肯定是知道的了,因为都知道前面有多少个大于它的),把这个数从1—n中去除,然后找前一个数,他的逆序对个数就是在去掉最后一个数的新数列中比它大的数的个数,又能找到它,依次往前,得出所有答案。
如果用权值线段树可以做到O(NlogN),但是我貌似搞忘了,所以二分+树状数组,复杂度O(NlogN2)一样能过。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int sum=0,f=1;    char ch;    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;isdigit(ch);ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}const int maxn=100010;int n,p[maxn],nixu[maxn],ans[maxn],pos;int tree[maxn];//树状数组下标的意思是在原序列中第几大,而查询某个位置得到的和是删了数之后它第几大int lowbit(int x){    return x & (-x);}int query(int x){    int res=0;    for(int i=x;i>=1;i-=lowbit(i))        res+=tree[i];    return res;}void add(int x,int w){    for(int i=x;i<=n;i+=lowbit(i))        tree[i]+=w;}int erfen(int x){    int l=1,r=n,mid;    while(l<=r)    {        mid=l+r>>1;//二分在原序列中第几大,在删过数的序列中是否是第逆序对数+1大        if(x>query(mid))//如果猜的这个原序列第几大小了,就l=mid+1            l=mid+1;        else//如果猜的这个原序列第几大大了,就r=mid-1            r=mid-1;    }    return l;}int main(){    freopen("premu.in","r",stdin);    freopen("premu.out","w",stdout);    n=getint();    for(int i=1;i<=n;++i)        p[i]=getint(),nixu[i]=p[i]-p[i-1];    for(int i=1;i<=n;++i)        add(i,1);    for(int i=n;i>=1;--i)    {        pos=erfen(nixu[i]+1);//二分查找,已知这个数在删了后面数的新序列第逆序对数+1大,求在原序列中第几大        ans[i]=n-pos+1;        add(pos,-1);//又把这个数给删掉(相当于比他小的数第几大都-1,也就是在删了数的序列中变大了)    }    for(int i=1;i<=n;++i)        printf("%d ",ans[i]);    return 0;}

本题结。

原创粉丝点击