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。
所有数据均有解。

分析

我们可以寻找日期逆序对的规律:当n=1时,只能记录一天,逆序对为0(这不用解释吧);当n=p(p>1)时,记ai表示当i为第一个数时,日历的第一个数以后的,比第一个数小的数字个数,bj表示当n=p-1时,第j天的日历的逆序对,则第(i-1)·(p-1)!+j天的日历的第一个数为i,后p-1个数字为第j天的日历,逆序对为ai+bj。而a有什么规律呢?[delete]没有[/delete]当然是ai=i-1。于是,我们可以递推求解。
然而你会发现这种算法的时间复杂度特别大,为O(n^2)。所以,我们需要对其进行优化。
我们发现,ai最大为n-1,最后一天的日历的逆序对最大,进而得出世界末日一定在第i▪j!(1<i≤j+1)天,其中j表示第n-j个数以前的数分别是1,2,3,…,n-j-1,i表示第n-j个数是n-j-1+i(请自己证明)。所以,我们可以用O(n)算法求出i和j的值,然后根据i和j求出世纪末日的日历的前n-j位,至于之后的数,就是除前n-j位以外的数的逆序(证明了前面那个,这个应该很容易了吧)。

代码

#include <bits/stdc++.h>

int n,m;
int x,y,z;

int main()
{
    scanf("%d%d",&n,&m);
    if (m == 0)
    {
        for (int i = 1; i <= n; i++)
            printf("%d ",i);
        return 0;
    }
    for (int i = 1; i <= n; i++)
        if (x + i <m)
            x += i;
        else
        {
            y = n - i - 1;
            break;
        }
    for (int i = 1; i <= y; i++)
        printf("%d ",i);
    z = y;
    for (;x <= m; x++)
        y++;
    printf("%d ",y);
    for (int i = n; i > z; i--)
        if (i != y)
            printf("%d ",i);
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值