[NOIP2017模拟]排列

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;
}

本题结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值