洛谷 P1338

https://www.luogu.org/problemnew/show/P1338

题目描述

只要是参加jsoi活动的同学一定都听说过Hanoi塔的传说:三根柱子上的金片每天被移动一次,当所有的金片都被移完之后,世界末日也就随之降临了。

在古老东方的幻想乡,人们都采用一种奇特的方式记录日期:他们用一些特殊的符号来表示从1开始的连续整数,1表示最小而N表示最大。创世纪的第一天,日历就被赋予了生命,它自动地开始计数,就像排列不断地增加。

我们用1-N来表示日历的元素,第一天日历就是

1, 2, 3, … N

第二天,日历自动变为

1, 2, 3, … N, N-1

……每次它都生成一个以前未出现过的“最小”的排列——把它转为N+1进制后数的数值最小。

日子一天一天地过着。有一天,一位预言者出现了——他预言道,当这个日历到达某个上帝安排的时刻,这个世界就会崩溃……他还预言到,假如某一个日期的逆序达到一个值M的时候,世界末日就要降临。

什么是逆序?日历中的两个不同符号,假如排在前面的那个比排在后面的那个更大,就是一个逆序,一个日期的逆序总数达到M后,末日就要降临,人们都期待一个贤者,能够预见那一天,到底将在什么时候到来?

输入输出格式

输入格式:

只包含一行两个正整数,分别为N和M。

输出格式:

输出一行,为世界末日的日期,每个数字之间用一个空格隔开。

输入输出样例

输入样例#1: 复制

5 4

输出样例#1: 复制

1 3 5 4 2

说明

对于10%的数据有N <= 10。

对于40%的数据有N <= 1000。

对于100%的数据有 N <= 50000。

所有数据均有解。

思路:首先要知道,对于1-n的n个数排列所产生的逆序对最多的情况为:n、n-1……1,共有1+2+……+n-1=n*(n-1)/2对。要使排列的字典序最小,那么自然希望把最小的数放到前面,我们只需要留i+1个位置,i满足1+2+……+i>=m的条件即可,这时对于前面n-i-1个位置,我们分别放上1、2……n-i-1。这时候考虑第n-i个位置,我们希望这个位置放的数越小越好,(剩余的位置按数字从大到小放置即可)而此时的备选数字有n-i、n-i+1……n,贡献的逆序对数分别为0、1……i。(即剩下的数中小于该数的数目) 又因为1+2+……+i>=m,那么自然有1+2+……+i-1<m,而1+2+……+i-1是第n-i个位置后面的i个位置贡献的逆序对数, 因此这个位置放的数字只需要贡献m-(1+2+ ……+i-1)个逆序对即可,因此这个位置要放置的数就是n-i+m-(1+2+ ……+i-1)

#include<iostream>
#include<cstdio>
using namespace std;

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int i;
    int temp=m;
    for(i=0;;i++)
    {
        if(i>=temp)
            break;
        temp-=i;
    }
    for(int j=1;j<n-i;j++)//留i+1个位置
        printf("%d ",j);
    temp=n-i+temp;
    printf("%d ",temp);
    for(int j=n;j>=n-i&&j>0;j--)
    {
        if(j==temp)
            continue;
        else
            printf("%d ",j);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/87803011
今日推荐