• 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;
    }
    
  • 相关阅读:
    ZJU_1145 OR POJ_1100 Dreisam Equations
    数据结构基础之队列
    The New Villa
    Mission Impossible 6
    数据结构基础之栈
    POJ_1185_炮兵阵地 dp+状态压缩
    (转载)Dig命令的用法
    (转载)服务发现系统etcd介绍
    golang 导入包
    1.4方程求根之弦截法
  • 原文地址:https://www.cnblogs.com/jklover/p/10091418.html
Copyright © 2020-2023  润新知