Written with StackEdit.
Description
大家应该都见过很多人手拉手围着篝火跳舞的场景吧?一般情况下,大家手
拉手跳舞总是会围成一个大圈,每个人的左手拉着旁边朋友的右手,右手拉着另一侧朋友的左手。
不过,如果每一个人都随机的拉住两个不同人的手,然后再慢慢散开,事情
就变得有趣多了——此时大家依旧会形成圈,不过却可能会形成多个独立的圈。
当然这里我们依然要求一个人的右手只能拉另一个人的左手,反之亦然。
班里一共有(N)个同学,由(1)到(N)编号。(Will)想知道,究竟有多少种本质不同的拉手方案,使得最终大家散开后恰好形成(k)个圈呢?
给定两种方案,若存在一个人和他的一只手,满足在这两种方案中,拉着这只手的人的编号不同,则这两种方案本质不同。
Input
输入一行包含三个正整数(N),(k)和(P).
(3<=3k<=N<=3000,10^4<=p<=2×10^9.)
Output
输出文件的包含一行一个整数,表示本质不同的方案数对(p)的余数。保证(p)一定是一个质数。
Sample Input
3 1 1000000009
Sample Output
2
Solution
- 这类游戏的套路其实都大同小异...考虑最后一个人的情况(dp)即可.
- 就此题而言,设(f[i][j])表示(i)个人形成(j)个环的方案数目.
- 考虑最后一个人,他可以插入到前面已经有的环里,不形成新的环.
- 前面(i-1)个人有(i-1)个位置可以插入.
- 方案数为(f[i-1][j]*(i-1)).
- 他也可以形成一个新的环,从前面的(i-1)个人中选择(2)个人和他成环,而这两人在他左右交换算作两次.
- 方案数为(f[i-3][j-1]*C_{i-1}^2*2.)
- 需要注意边界条件,显然有(f[0][0]=1),还需要注意的是当(j<lfloor i/3 floor)时,因为至少要三个人组成一个环,这种情况的方案数也为(0).
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
int n,k,P;
inline int add(int a,int b)
{
return (1LL*a + 1LL*b) % P;
}
inline int mul(int a,int b)
{
return 1LL * a * b % P;
}
inline int swapC(int x)
{
return mul(x,x-1);
}
const int MAXN=3e3+110;
int f[MAXN][MAXN];
int dp(int i,int j)
{
if(f[i][j]!=-1)
return f[i][j];
if(i==0 && j==0)
return 0;
if(j>(i/3))//注意边界
return 0;
int &res=f[i][j];
res=0;
res=add(res,mul(dp(i-1,j),i-1));
res=add(res,mul(dp(i-3,j-1),swapC(i-1)));
return res;
}
int main()
{
n=read(),k=read(),P=read();
for(int i=0;i<=n;++i)
for(int j=0;j<=k;++j)
f[i][j]=-1;
int ans=dp(n,k);
printf("%d
",ans);
return 0;
}