题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2062

推荐博客(详细解析):

https://blog.csdn.net/qq_33266889/article/details/53468509 (讲的更易懂)

https://blog.csdn.net/lianqi15571/article/details/8877014(分析推导比较清楚)

code:

#include <cstdio>

using namespace std;
typedef long long LL;

LL n;  // 元素的个数
LL m;  
int s[20+7];  // 分组后每组的首元素

LL g[20+7];  // 分组后每组子集的个数

void init()
{
    g[0] = 0;
    for(int i=1; i<27; i++)
        g[i] = g[i-1] * (i-1) + 1;
}

int main()
{
    init();
    while(scanf("%lld %lld", &n, &m) != EOF)
    {
        for(int i=0; i<27; i++)
            s[i] = i;
        while(n>0 && m>0) // 只有n个元素最多执行n次,或者执行到m=0
        {
            // 第m个子集在分组后在第t组
            int t = m / g[n] + (m%g[n]>0?1:0);
            if(t > 0)
            {
                printf("%d", s[t]);
                for(int i=t; i<=n; i++)     // 删除i以后,i之后的要加一,比如删除2,那么下次的第二组开头是3
                    s[i] = s[i+1];
                // 减去1 - t-1组无用的和删除i后当前组第一个元素变成的空集。方便在当前组内重新分组,并重新定位t
                m = m - ((t-1)*g[n] + 1);  
                if(m == 0)  printf("\n");   // 格式控制
                else printf(" ");
            }
             n --;   // 循环每执行一次减一,因为删除了一个元素
        }
       
    }


    return 0;
}