题目大意:
在 $n \log n$ 求最长上升子序列的算法中用到的 $d$ 数组上进行三进制压位,$0$ 表示该数字还未访问到,$1$ 表示该数字访问到了且仍在 $d$ 数组中,$2$ 表示访问过来且已被弹出 $d$ 数组。
由于每次转移是 $0$ 变成 $1$,$1$ 变成 $2$,因此从小到大枚举状态即可。
#include <cstdio>
const int N = 14348910;
int f[N] = {1}, p[20] = {1}, a[20], b[20], n, m, ans;
bool check(int x) { //有m个1, n-m个2
int i = 0, j = 0;
while (x) i += (x % 3 == 1), j += (x % 3 == 2), x /= 3;
if (i == m && j == n - m) return true; return false;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i)
scanf("%d", &a[i]), b[a[i]] = i;
for (int i = 1; i <= n; ++i) p[i] = p[i-1] * 3;
for (int x = 0; x < p[n]; ++x) { //枚举当前状态
int t = x, s = 0;
while (t) s += (t % 3 == 1), t /= 3;
if (s > m) continue;
if (check(x)) ans += f[x];
for (int j = 1; j <= n; ++j) { //枚举新添的元素
if (x / p[j-1] % 3) continue; //判断是否已经访问过了
if (b[j] > 1 && !(x / p[a[b[j]-1]-1] % 3)) continue; //判断LIS中上一个元素是否已经访问过
int y = x + p[j-1];
for (int k = j + 1; k <= n; ++k) //寻找被替换的元素
if (x / p[k-1] % 3 == 1) { y += p[k-1]; break; }
f[y] += f[x];
}
}
printf("%d\n", ans);
return 0;
}
时间复杂度 $O(3^nn^2)$