题意:
有 n n n台电脑,手动打开电脑 i − 1 i-1 i−1和 i + 1 i+1 i+1 ,那么电脑 i i i 就会被自动打开。求有多少种手动打开电脑的方案,使得最后全部电脑都被打开,打开电脑顺序不同视为不同的方案。
题解:
显然,第一台电脑和最后一台电脑必定是要手动打开的。
设 d p [ i ] [ j ] dp[i][j] dp[i][j]为前 i i i 台电脑打开 j j j 台且第 i i i台被打开的方案数。
设最后有 k ( ≥ 1 ) k(\geq 1) k(≥1)台连续的电脑被打开,对 k k k台电脑的先后顺序进行讨论:
先打开第1个,那么后面只能按顺序来,方案数为 1 1 1
先打开第2个,后面只能按顺序来,前面可以任意插入,方案数为 C ( k − 1 , 1 ) C(k-1,1) C(k−1,1)
先打开第3个,前面按顺序来,后面也按顺序来,前后可以互相插入,方案数为 C ( k − 1 , 2 ) C(k-1,2) C(k−1,2)
以此类推,总方案数为 C ( k − 1 , 0 ) + C ( k − 1 , 1 ) + C ( k − 1 , 2 ) + . . . . + C ( k − 1 , k − 1 ) = 2 k − 1 C(k-1,0)+C(k-1,1)+C(k-1,2)+....+C(k-1,k-1)=2^{k-1} C(k−1,0)+C(k−1,1)+C(k−1,2)+....+C(k−1,k−1)=2k−1
因为最后有连续 k k k个,那么前 i − k i-k i−k 就要打开 j − k j-k j−k 个,又因为最后只有 k k k个连续,那么第 i − k i-k i−k必定是未打开的,且 i − k − 1 i-k-1 i−k−1一定是打开的,那么方案数为 d p [ i − k − 1 ] [ j − k ] dp[i-k-1][j-k] dp[i−k−1][j−k] ,这一部分和最后的部分是独立的,最后要结合在一起,就是插空,方案数为 C ( j , k ) C(j,k) C(j,k) ,那么就可以推出状态转移方程了: d p [ i ] [ j ] = ∑ d p [ i − k − 1 ] [ j − k ] ⋅ C ( j , k ) ⋅ 2 k − 1 dp[i][j]=\sum dp[i-k-1][j-k] \cdot C(j,k) \cdot 2^{k-1} dp[i][j]=∑dp[i−k−1][j−k]⋅C(j,k)⋅2k−1
最后要注意一些边界初始化。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=4e2+5;
const int inf=0x3f3f3f3f;
ll dp[MAXN][MAXN];
ll c[MAXN][MAXN];
ll qpow(ll a,ll b,ll m)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%m;
a=a*a%m;
b>>=1;
}
return res;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
c[i][0]=1;
c[i][1]=i;
}
for(int i=2;i<=n;i++)
{
for(int j=2;j<=i;j++)
{
c[i][j]=(c[i-1][j-1]+c[i-1][j])%m;
}
}
dp[1][1]=1;
dp[2][2]=2;
for(int i=3;i<=n;i++)
{
dp[i][i]=qpow(2,i-1,m);
}
for(int i=3;i<=n;i++)
{
for(int j=1;j<i;j++)
{
for(int k=1;k<j;k++)
{
dp[i][j]=(dp[i][j]+dp[i-k-1][j-k]*c[j][k]%m*qpow(2,k-1,m)%m)%m;
}
}
}
ll ans=0;
for(int i=1;i<=n;i++)
{
//cout<<i<<" "<<dp[n][i]<<endl;
ans=(ans+dp[n][i])%m;
}
cout<<ans<<endl;
}