有 n 个 电 脑 排 在 一 排 , 你 可 以 手 动 打 开 任 意 电 脑 , 但 是 有 个 特 点 , 有n个电脑排在一排,你可以手动打开任意电脑,但是有个特点, 有n个电脑排在一排,你可以手动打开任意电脑,但是有个特点,
对 于 一 个 电 脑 , 如 果 它 左 右 都 被 打 开 了 , 那 么 它 会 自 动 打 开 。 对于一个电脑,如果它左右都被打开了,那么它会自动打开。 对于一个电脑,如果它左右都被打开了,那么它会自动打开。
最 后 问 , 有 多 少 种 全 部 开 机 方 案 ? 最后问,有多少种全部开机方案? 最后问,有多少种全部开机方案?
我 们 考 虑 两 种 方 式 , 第 一 种 全 部 都 是 手 动 打 开 , 第 二 种 带 有 自 动 打 开 。 我们考虑两种方式,第一种全部都是手动打开,第二种带有自动打开。 我们考虑两种方式,第一种全部都是手动打开,第二种带有自动打开。
对 于 电 脑 1 , 如 果 要 全 部 手 动 开 启 , 则 需 要 按 照 1 , 2... n 的 顺 序 打 开 , C n − 1 0 。 对于电脑1,如果要全部手动开启,则需要按照1,2...n的顺序打开,C_{n-1}^0。 对于电脑1,如果要全部手动开启,则需要按照1,2...n的顺序打开,Cn−10。
对 于 电 脑 2 , 如 果 要 全 部 手 动 开 启 , 则 后 面 必 须 按 照 3 , 4.. n 的 顺 序 , 前 面 必 须 1 , C n − 1 1 对于电脑2,如果要全部手动开启,则后面必须按照3,4..n的顺序,前面必须1,C_{n-1}^1 对于电脑2,如果要全部手动开启,则后面必须按照3,4..n的顺序,前面必须1,Cn−11
对 于 电 脑 k , 如 果 要 全 部 手 动 开 启 , 则 后 面 必 须 要 按 照 k + 1 , k + 2... n 的 顺 序 , 前 面 必 须 是 k − 1 , . . . 1 的 顺 序 , 所 有 要 在 n − 1 中 选 k − 1 个 开 前 面 , C n − 1 k − 1 对于电脑k,如果要全部手动开启,则后面必须要按照k+1,k+2...n的顺序,前面必须是k-1,...1的顺序,所有要在n-1中选k-1个开前面,C_{n-1}^{k-1} 对于电脑k,如果要全部手动开启,则后面必须要按照k+1,k+2...n的顺序,前面必须是k−1,...1的顺序,所有要在n−1中选k−1个开前面,Cn−1k−1
则 总 方 案 数 为 C n − 1 0 + C n − 1 1 + . . . + C n − 1 n − 1 = 2 n − 1 则总方案数为C_{n-1}^0+C_{n-1}^1+...+C_{n-1}^{n-1}=2^{n-1} 则总方案数为Cn−10+Cn−11+...+Cn−1n−1=2n−1
也 就 是 说 , 对 于 x 台 电 脑 , 如 果 全 部 手 动 开 启 的 话 , 有 2 x − 1 种 方 案 数 , 下 面 会 用 到 。 也就是说,对于x台电脑,如果全部手动开启的话,有2^{x-1}种方案数,下面会用到。 也就是说,对于x台电脑,如果全部手动开启的话,有2x−1种方案数,下面会用到。
自 动 打 开 只 会 出 现 现 在 左 右 都 打 开 后 才 会 自 动 打 开 , 所 以 假 设 这 么 一 种 开 机 方 式 : 自动打开只会出现现在左右都打开后才会自动打开,所以假设这么一种开机方式: 自动打开只会出现现在左右都打开后才会自动打开,所以假设这么一种开机方式:
设 有 x k 台 电 脑 自 动 开 启 。 设有x_k台电脑自动开启。 设有xk台电脑自动开启。
1 到 x 1 − 1 手 动 打 开 , x 1 + 1 到 x 2 − 1 手 动 打 开 , . . . x k + 1 到 n 手 动 打 开 。 ( x k + 1 ≤ N ) 1到x_1-1手动打开,x1+1到x_2-1手动打开,...x_k+1到n手动打开。(x_k+1\leq N) 1到x1−1手动打开,x1+1到x2−1手动打开,...xk+1到n手动打开。(xk+1≤N)
这 样 , x k 台 都 满 足 自 动 打 开 的 要 求 。 这样,x_k台都满足自动打开的要求。 这样,xk台都满足自动打开的要求。
然 后 就 是 要 怎 么 进 行 这 个 过 程 。 然后就是要怎么进行这个过程。 然后就是要怎么进行这个过程。
设 f [ i ] [ j ] 为 前 i 台 电 脑 中 , 有 j 台 是 手 动 打 开 , 第 i 台 也 是 手 动 打 开 , 并 且 第 i + 1 台 自 动 开 启 的 方 案 数 。 设f[i][j]为前i台电脑中,有j台是手动打开,第i台也是手动打开,并且第i+1台自动开启的方案数。 设f[i][j]为前i台电脑中,有j台是手动打开,第i台也是手动打开,并且第i+1台自动开启的方案数。
则 我 们 想 让 第 i + 1 台 成 为 自 动 打 开 的 电 脑 , 则 i + 1 后 面 几 台 电 脑 手 动 打 开 , 设 为 k 台 。 则我们想让第i+1台成为自动打开的电脑,则i+1后面几台电脑手动打开,设为k台。 则我们想让第i+1台成为自动打开的电脑,则i+1后面几台电脑手动打开,设为k台。
则 就 是 f [ i ] [ j ] 向 f [ i + 1 + k ] [ j + k ] 的 转 移 , 怎 么 转 移 呢 ? 则就是f[i][j]向f[i+1+k][j+k]的转移,怎么转移呢? 则就是f[i][j]向f[i+1+k][j+k]的转移,怎么转移呢?
f [ i + 1 + k ] [ j + k ] 比 f [ i ] [ j ] 多 了 k 台 手 动 开 启 。 f[i+1+k][j+k]比f[i][j]多了k台手动开启。 f[i+1+k][j+k]比f[i][j]多了k台手动开启。
这 k 台 我 们 称 为 “ 新 ” 打 开 的 电 脑 , j 台 我 们 称 为 “ 旧 ” 打 开 的 电 脑 。 这k台我们称为“新”打开的电脑,j台我们称为“旧”打开的电脑。 这k台我们称为“新”打开的电脑,j台我们称为“旧”打开的电脑。
第 i + 1 是 自 动 打 开 的 。 第i+1是自动打开的。 第i+1是自动打开的。
之 前 我 们 处 理 过 , k 台 手 动 开 启 的 方 案 数 为 2 k − 1 , 然 后 我 们 要 将 k 台 和 j 台 一 起 合 并 。 之前我们处理过,k台手动开启的方案数为2^{k-1},然后我们要将k台和j台一起合并。 之前我们处理过,k台手动开启的方案数为2k−1,然后我们要将k台和j台一起合并。
那 么 就 是 有 j + k 台 中 , 有 k 台 是 “ 新 ” 电 脑 , 方 案 数 为 C k + j k 。 那么就是有j+k台中,有k台是“新”电脑,方案数为C_{k+j}^k。 那么就是有j+k台中,有k台是“新”电脑,方案数为Ck+jk。
则 转 移 方 程 为 f [ i + 1 + k ] [ j + k ] = f [ i ] [ j ] ∗ 2 k − 1 ∗ C j + k k 。 则转移方程为f[i+1+k][j+k]=f[i][j]*2^{k-1}*C_{j+k}^k。 则转移方程为f[i+1+k][j+k]=f[i][j]∗2k−1∗Cj+kk。
i 、 j 、 k 三 层 循 环 进 行 转 移 , O ( n 3 ) 是 可 以 的 。 i、j、k三层循环进行转移,O(n^3)是可以的。 i、j、k三层循环进行转移,O(n3)是可以的。
最 后 预 处 理 组 合 数 进 行 转 移 即 可 。 最后预处理组合数进行转移即可。 最后预处理组合数进行转移即可。
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
ll n, mod;
const int N = 410;
ll F[N], invn[N], invF[N], bit[N];
void init() {
bit[1] = F[0] = F[1] = invn[0] = invn[1] = invF[0] = invF[1] = 1;
for(int i = 2;i < N; i++){
F[i] = F[i - 1] * i % mod;
invn[i] = (mod - mod / i) * invn[mod % i] % mod;
invF[i] = invF[i - 1] * invn[i] % mod;
bit[i] = bit[i - 1] * 2 % mod;
}
}
ll C(ll m, ll n) {
if(m < 0 || n < 0 || n > m)
return 0;
ll ans = F[m];
ans = ans * invF[n] % mod;
ans = ans * invF[m - n] % mod;
return ans;
}
void solve() {
cin >> n >> mod;
init();
vector<vector<ll>> dp(n + 1, vector<ll>(n + 1, 0));
for(int i = 1;i <= n; i++) {
// 前i台电脑
dp[i][i] = bit[i];
for(int j = 0;j <= i; j++) {
// i台中有j台是手动打开的
for(int k = 1;k + i + 1 <= n; k++) {
// i + 1是自动打开的,然后后面k台需要手动打开
dp[i + 1 + k][j + k] = (dp[i + 1 + k][j + k] + dp[i][j] * bit[k] % mod * C(j + k, k) % mod) % mod;
}
}
}
ll ans = 0;
for(int i = 0;i <= n; i++) {
ans = (ans + dp[n][i]) % mod;
}
cout << ans << endl;
}
signed main() {
solve();
}