bzoj 4465 游戏中的学问

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;
}
原文地址:https://www.cnblogs.com/jklover/p/10091418.html