题目大意:有这样一个排序:
for(cnt=0;!_sorted_(a,n);cnt++)
for(int i=n-1;i;i--) if(a[i]>a[i+1]) swap(a[i],a[i+1]);
给定n,问在所有排列p中,第k小的满足执行上述代码后cnt=m的排列是啥。n<=20。
题解:结论是,给定一个排列,那个cnt就是,对每个位置求后面有多少个数字比这个数字小,的max。f(n,m)表示n个数cnt不超过m的方案数是小学生计数。然后逐位枚举确定即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lint long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int del[30],ans[30];
inline lint fac(int n,lint ans=1) { rep(i,1,n) ans*=i;return ans; }
inline lint _pow_(int a,int b,lint ans=1) { rep(i,1,b) ans*=a;return ans; }
inline lint f(int n,int m) { return (n<=m)?fac(n):_pow_(m+1,n-m)*fac(m); }
inline int kth(int k,int p=1) { for(;k-(!del[p]);p++) k-=!del[p];return p; }
int main()
{
int n,m,has_mp1=0;lint k;scanf("%d%d%lld",&n,&m,&k);
if(m==0) { rep(i,1,n) printf("%d ",i);return !printf("\n"); }
rep(i,1,n) rep(j,1,min(n-i+1,m+1))
{ has_mp1|=(j==m+1);lint cnt=f(n-i,m)-(has_mp1==0)*f(n-i,m-1);
if(k>cnt) k-=cnt;else { del[ans[i]=kth(j)]=1;break; } }
rep(i,1,n) printf("%d ",ans[i]);return !printf("\n");
}