逆序对

谈到逆序对,最近看到一个比较有意思的题目,随手就记了下来。

先来简单说一下逆序对的概念吧,有一个序列a[n],存在( a[i] > a[j] && i < j )这就是一个逆序对。求逆序对的一个方法就是利用归并排序,归并排序有一点比较难理解,主要利用了递归和分治的方法来实现,我就不过都赘述了,可以看一看这位的分享,写的很详实了:归并求逆序对

洛谷P1338末日的传说

这次看到了一个这样的题目:在古老东方的幻想乡,人们都采用一种奇特的方式记录日期:他们用一些特殊的符号来表示从1开始的连续整数,1表示最小而N表示最大。创世纪的第一天,日历就被赋予了生命,它自动地开始计数,就像排列不断地增加。我们用1-N来表示日历的元素,第一天日历就是1, 2, 3, … N第二天,日历自动变为1, 2, 3, … N, N-1……每次它都生成一个以前未出现过的“最小”的排列——把它转为N+1进制后数的数值最小。日子一天一天地过着。有一天,一位预言者出现了——他预言道,当这个日历到达某个上帝安排的时刻,这个世界就会崩溃……他还预言到,假如某一个日期的逆序达到一个值M的时候,世界末日就要降临。什么是逆序?日历中的两个不同符号,假如排在前面的那个比排在后面的那个更大,就是一个逆序,一个日期的逆序总数达到M后,末日就要降临,人们都期待一个贤者,能够预见那一天,到底将在什么时候到来?

输入:

5 4

输出:

1 3 5 4 2

这一题想通了的话其实是非常简单的,首先想看题义是要求求出存在m个逆序对的情况下最小的排序序列(字典序排列)。其实我们可以很容易地发现当逆序对越多的情况下序列的字典序排列是越来越大的。至此,根据规律推断可以知道一个长度为n的序列的逆序对数量最大为(n-1)*n/2,而每次如果长度为n-1的序列的最大逆序对数量比m要小的话,就需要把最大的数放在长度为n的序列的最前面,这样逆序对才会增加,且使得大数处在序列的前面(也就是数列的字典序更大)的机会减少了,求出来的使得逆序对数为m的序列的字典序就是最小的。当然如果相反的话,就把长度为n的序列的最小值放在序列的最后面,不增加逆序对的个数(别忘了每次如果(n-i-1)*(n-i)/2<m时,m-=当前序列长度(在这里的表示就是n))

如果还有疑问的话可以好好看看代码再理解理解:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int a[500005];
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll f=1,l=n;
    for(ll i=1;i<=n;i++)
    {
        ll t=(ll)(n-i)*(n-i-1)/2;
        if(t>=m)
            a[f++]=i;
        else a[l--]=i,m-=(l-f+1);
    }
    for(int i=1;i<=n;i++)
    printf("%d%c",a[i],(i==n)?'\n':' ');
    // system("pause");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值