题目

Description
Flowey 是一朵能够通过友谊颗粒传播LOVE 的小花.它的友谊颗粒分为两种,
圆粒的和皱粒的,它们依次排列组成了一个长度为2m 的序列.对于一个友谊颗
粒的序列,如果存在1<=i<j<=2m,满足以下条件:
1)i 为偶数,j 为奇数
2)第i 颗友谊颗粒和第j 颗友谊颗粒同为圆粒或同为皱粒
3)第i 颗友谊颗粒和第j 颗友谊颗粒都还没有被使用过
那么,就可以使用这两颗友谊颗粒,然后提升一次LV.
定义一个友谊颗粒的序列为高效的,当且仅当尽可能多的提升LV 后,序列
上剩余的友谊颗粒数量不超过2n。
现在,Flowey 想知道,长度为2m 的友谊颗粒序列,有多少个不同的序列是
高效的?
定义两个友谊颗粒序列是不同的,当且仅当存在1<=i<=2m,第i 颗友谊颗粒在
一个序列中为圆粒,而在另一个中为皱粒.
由于答案可能很大,你只需要求出答案对p 取模的结果.

Input
从friend.in 读入数据
为一行三个正整数,n,m,p

Output
输出到friend.out 中
为一行一个非负整数,代表答案对p 取模的结果

Sample Input
输入1:
2 3 233
输入2:
223 514 514223
输入3:
50 25 514223

Sample Output
输出1:
56
输出2:
488817
输出3:
140210

Data Constraint
对于30%的数据,满足n<=10,m<=10
对于60%的数据,满足n<=300,m<=300
对于100%的数据,满足1<=n<=3000,1<=m<=3000,2<=p<=1000000007

思路

考虑暴力,设F[i][j][k]为前i组,剩j个0,k个1
假设一开始我们有n个可匹配的偶数位置(可0可1),我们会发现我们转移过程中可供匹配的位置数量恒为n
那么修改一下状态,F[i][j]表示前i组有j个0,那么就有n-j个1
但是这样会算重,一个合法方案可能有很多种分配n个可匹配的位置的01的方案
注意到一定有一种方案满足某个时刻可匹配的位置用完了
再加一维0/1表示转移过程中是否经过j=0

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,p;
ll f[3100][3100][2];
int main()
{
	freopen("friend.in","r",stdin);
	freopen("friend.out","w",stdout);
	scanf("%d%d%d",&n,&m,&p);
	for(int j=1;j<=n;j++)
		f[0][j][0]=1;
	f[0][0][1]=1;	
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<=n;j++)
		{
			f[i][j][0]=f[i][j][1]=0;
			if(j==0) f[i][j][1]+=f[i-1][j+1][0];
			if(j>0) f[i][j][1]+=f[i-1][j][1]+f[i-1][j-1][1];
			if(j==1) f[i][j][1]+=f[i-1][j][0]+f[i-1][j-1][0];
			if(j>1) f[i][j][0]+=f[i-1][j][0]+f[i-1][j-1][0];
			
			if(j<n) 
			{
				f[i][j][1]+=f[i-1][j][1]+f[i-1][j+1][1];
				f[i][j][0]+=f[i-1][j][0]+f[i-1][j+1][0];
			}

			f[i][j][0]%=p;f[i][j][1]%=p;
		}
		f[i][0][0]=0;
	}
				
	ll ans=0;
	for(int i=0;i<=n;i++) ans=(ans+f[m][i][1])%p;
	printf("%lld",ans);					
	return 0;
}