1 条题解

  • 0
    @ 2025-8-24 21:20:48

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar yummy
    这个人是时代的眼泪,什么也没有留下

    搬运于2025-08-24 21:20:47,当前版本为作者最后更新于2018-10-01 11:37:36,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    简单地逛了逛题解区,发现大部分题解分为两种:

    • next_permutation
    • 手写 next_permutation

    于是,我为了不让题解区太单调,也为了拓宽大家的思维,就经过几分钟的努力,想出了一种与众不同的方法。

    Updated on 2022.7.14: 添加 LaTeX\LaTeX,增加变进制数加法的讲解。

    变进制数

    我们的目标是把全排列转化成一个变进制数,以方便我们进行加法。
    对于第 ii 根手指,它有 ni+1n-i+1 种选择,根据位值原理,要想让每个数对应一个全排列,就要让这一位数是 ni+1n-i+1 进制的。

    那么,整个过程分为三步:

    1. 将火星数变成变进制数
    2. 将变进制数加上 mm
    3. 将变进制数变成火星数

    我们来看一个实例: 将 1,4,5,2,31,4,5,2,3 变成变进制数:

    • 首位 1155 种选择 {1,2,3,4,5}\{1,2,3,4,5\} 的第 11 种,故变为 00(从0开始)
    • 次位 4444 种选择 {2,3,4,5}\{2,3,4,5\} 的第 33 种,故变为 22
    • 中间位 5533 种选择 {2,3,5}\{2,3,5\} 的第 33 种,故变为 22
    • 次低位 2222 种选择 {2,3}\{2,3\} 的第 11 种,故变为 00
    • 末位 3311 种选择 {3}\{3\} 的第 11 种,故变为 00
    • 最后,排列 1,4,5,2,31,4,5,2,3 变成了(02200)unknown(02200)_{unknown}

    接下来给它加上 33 变成 (02203)(02203),并处理进位:

    • 末位是 11 进制的,进 33(02230)(02230)
    • 次低位是 22 进制的,满 22 进一得 (02310)(02310)
    • 中间位是 33 进制的,满 33 进一得 (03010)(03010)
    • 次位是 44 进制的,3<43<4,不进位,得 (03010)unknown(03010)_{unknown}

    最后将 (03010)unknown(03010)_{unknown} 变回火星数。

    • 首位 00 表示这位应选择 {1,2,3,4,5}\{1,2,3,4,5\} 的第 11 种,即 11
    • 次位 33 表示这位应选择 {2,3,4,5}\{2,3,4,5\} 的第 44 种(11 被选过了),即 55
    • 中间位 00 表示这位应选择 {2,3,4}\{2,3,4\} 的第 11 种,即 22
    • 次低位 11 表示这位应选择 {3,4}\{3,4\} 的第 22 种,即 44
    • 末位 00 表示这位应选择 {3}\{3\} 的第 11 种,即 33

    所以本题答案为 14523 +3=+3= 15243

    代码 3737 行,应该是除 STL 外较短的了。

    #include<bits/stdc++.h>
    using namespace std;
    int a[10005];
    bool used[10005]={0};
    int m,n;
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            int x=a[i];
            for(int j=1;j<=a[i];j++)
                x-=used[j];
            used[a[i]]=1;
            a[i]=x-1;
        }
        a[n]+=m;
        for(int i=n;i>0;i--)
        {
            a[i-1]+=a[i]/(n-i+1);
            a[i]%=n-i+1;
        }
        memset(used,0,sizeof(used));
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=a[i];j++)
                if(used[j])
                    a[i]++;
            cout<<a[i]+1<<" ";
            used[a[i]]=1;
        }
        return 0;
    }
    

    这篇题解是我较早时候发的,通过评论区我纠正了举个栗子那个部分的小问题。
    现在我也通过评论区知道我这方法叫做康托展开。如果你想了解更多,可以看这篇博客.

    • 1

    信息

    ID
    90
    时间
    1000ms
    内存
    125MiB
    难度
    2
    标签
    递交数
    1
    已通过
    1
    上传者