1.タイトルリンク:
POJ-1037
2.トピックの主なアイデア:
2つの整数n、cを入力します
語彙の順序cで1〜nの**配置を出力する必要があります。
**配置とは、長さがn、各番号が1〜nで、番号が繰り返されず、サイズがずらされた配置を指します。
3.分析:
「トライアル法」は、cのランキングを決定するために使用することができます。
具体的には、最初の数字のサイズを列挙できます
最初の数がa [1]の場合、n-1個の数で構成される**置換スキームの数をtとします。
t> = cの場合、最初の数値はa [1]です。
それ以外の場合は、最初の数値を大きくする必要があることを意味し、c-t、a [1]を1ずつ増やします。
上記のプロセスを繰り返して最初の数値のサイズを決定し、同様にすべての数値のサイズを決定します。
したがって、最初にtを前処理する必要があります。
f [i] [j] [k]は、i個の相互に異なる番号を表します。ここで、左端の番号はi個の番号の中で昇順でjにランク付けされ、左端の番号はlow(0)/ high( 1)ビット、1時間あたりのプログラム数。
状態遷移方程式は非常に単純です。詳細については、コードを参照してください。
PS:最初の数字のサイズを決定するときは、最初にf [n] [j] [1]を列挙し、次にf [n] [j] [1]を列挙する必要があります。これは、両方が条件を満たす場合だからです。の場合、1が高い語彙の順序が小さいことは明らかです。
4.コードの実装:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int M = (int)20;
bool vis[M + 5];
ll f[M + 5][M + 5][2];
void init()
{
f[1][1][0] = f[1][1][1] = 1;
for(int i = 2; i <= M; ++i)
{
for(int j = 1; j <= i; ++j)
{
for(int k = j; k <= i - 1; ++k) f[i][j][0] += f[i - 1][k][1];
for(int k = 1; k <= j - 1; ++k) f[i][j][1] += f[i - 1][k][0];
}
}
}
int main()
{
init();
int T;
scanf("%d", &T);
while(T--)
{
memset(vis, 0, sizeof(vis));
int n; ll c;
scanf("%d %lld", &n, &c);
int ls, k;
for(int j = 1; j <= n; ++j)
{
if(f[n][j][1] >= c)
{
ls = j;
k = 1;
break;
}
else c -= f[n][j][1];
if(f[n][j][0] >= c)
{
ls = j;
k = 0;
break;
}
else c -= f[n][j][0];
}
vis[ls] = 1;
printf("%d", ls);
for(int i = 2; i <= n; ++i)
{
k ^= 1;
int j = 0;
for(int l = 1; l <= n; ++l)
{
if(vis[l]) continue;
++j;
if(k == 0 && ls > l || k == 1 && ls < l)
{
if(f[n - i + 1][j][k] >= c)
{
ls = l;
break;
}
else c -= f[n - i + 1][j][k];
}
}
vis[ls] = 1;
printf(" %d", ls);
}
printf("\n");
}
return 0;
}