zoj2425

题目大意:

一个整数序列a1, a2, … , an的倒置数是数对(ai, aj)满足i < j并且ai > aj.给定n和倒置数m,你的任务是找到集合最小的序列{1,2,…,m},倒置数正好为m。

解题思路:

仔细观察下面的几组数据,每行分别表示invertion number和对应的一组最小的排列:
1: 2 1
2: 2 3 1
3: 3 2 1
4: 2 4 3 1
5: 3 4 2 1
6: 4 3 2 1
7: 2 5 4 3 1
8: 3 5 4 2 1
9: 4 5 3 2 1
10: 5 4 3 2 1
11: 2 6 5 4 3 1
12: 3 6 5 4 2 1
13: 4 6 5 3 2 1
14: 5 6 4 3 2 1
15: 6 5 4 3 2 1
相信很容易就看出了规律吧。首先,相同长度之间排列的规律,其次就是每组长度相同的数据中最后一个invertion number与长度的关系。哈哈,很简单吧。然后根据n,把前面的几个数从小到大输出来,然后将对应的invertion number的排列数的每个数加上前面已经输出的数的个数t,就是所求答案了。
当然了,记录这些排列显然内存是不够的,而且n的最大值为50000,是在太大,这时候,相同长度之间的规律就派上用场了。
我们只需要用一个数组list,以长度为下标,每组最后一个invertion number存起来,list[2]=1, list[3]=3,
list[4]=6, list[5]=10, list[6]=15, …时间复杂度O(n)
这样,给定m后,就可以通过二分查找,用O(logn)的时间找到对应的长度。然后根据所找到的规律,将结果打印出来,时间复杂度O(n)
这样,总的时间复杂度为O(n)。

代码如下:

#include<iostream>

using namespace std;

int n,m, id;
int list[50001];

int find(int num)
{
    int L=1, R=n, mid;
    while(L<R-1)
    {
        mid=L+R>>1;
        if(list[mid]>= num) R=mid;
        else   L=mid;
    }
    return L;
}

int main()
{

    for(int i=1; i<50000; ++i)
        list[i+1]=list[i] + i;

    while(1)
    {
        cin>>n>>m;
        if(n==-1 && m==-1)  break;

        if(m==0)
        {
            cout<<1;
            for(int i=2; i<=n; ++i)
                cout<<' '<<i;
            cout<<endl;
        }
        else
        {
            id=find(m);

            int end=n-id;
            int num;

            for(int i=1; i<end; ++i)
                cout<<i<<' ';

            num=m-list[id];
            cout<<(num+end);
            for(int i=id; i>=0 ;--i)
                if(num!=i)
                    cout<<' '<<(i+end);
            cout<<endl;
        }
    }

    return 0;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值