1.タイトルリンク:
POJ-3208
2.トピックの主なアイデア:
悪魔の数を666を連続して含む数として定義します。
k番目に多い悪魔の数を見つけます。
3.分析:
明らかに、デジタルDPを解決するためにピット充填法を使用してください。
現在の列挙が番号のi番目の位置に到達し、i番目の位置がjであると仮定すると、現在の列挙された番号以下のデーモンの数を知る必要があります。
明らかに、f [i] [j]を前処理する必要があります。
f [i] [0]:i桁以下で、i番目の桁から6個の非悪魔番号が連続して0個ある非悪魔番号の数。
f [i] [1]:i桁以下で、i番目の桁から6が連続している非悪魔番号の数。
f [i] [2]:i桁以下の非悪魔番号と、i番目の桁から始まる2つの連続する6つの非悪魔番号の数。
f [i] [3]:i桁を超えない悪魔の数。
アクセス可能な状態遷移方程式は次のとおりです。
f [i] [0] =(f [i-1] [0] + f [i-1] [1] + f [i-1] [2])* 9
f [i] [1] = f [i-1] [0]
f [i] [2] = f [i -1] [1]
f [i] [3] = f [i-1] [2] + f [i-1] [3] * 10
詳細については、コードを参照してください。
4.コードの実装:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll f[25][4];
void init()
{
f[0][0] = 1;
for(int i = 1; i <= 20; ++i)
{
f[i][0] = (f[i - 1][0] + f[i - 1][1] + f[i - 1][2]) * 9;
f[i][1] = f[i - 1][0];
f[i][2] = f[i - 1][1];
f[i][3] = f[i - 1][2] + f[i - 1][3] * 10;
}
}
int main()
{
init();
int T;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d", &n);
for(m = 1; f[m][3] < n; ++m);
for(int i = m, k = 0; i >= 1; --i)
{
for(int j = 0; j <= 9; ++j)
{
ll cnt = f[i - 1][3];
if(j == 6 || k == 3)
{
for(int l = max(0, 3 - k - (j == 6)); l < 3; ++l)
{
cnt += f[i - 1][l];
}
}
if(cnt < n) n -= cnt;
else
{
if(k < 3)
{
if(j == 6) ++k;
else k = 0;
}
printf("%d", j);
break;
}
}
}
printf("\n");
}
return 0;
}