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 取模的结果.
对于60%的数据,满足n<=300,m<=300
对于100%的数据,满足1<=n<=3000,1<=m<=3000,p<=1e9+7
抽象题意
将奇偶分开,偶一排放上面,奇一排放下面,球有颜色,上下同色匹配(匹配形如“\”或“|”),要求匹配数>=n,求给球赋值颜色的方案数
60%
dp显然
f[i][j][k]表示下面到第i个,上面剩余失配白色数为j,失配黑色数为k
枚举当前第i列上下颜色转移
状态O(n^3)转移O(1)
100%
不dp很难做,着手于优化dp状态
可以发现,有一个约束条件j+k<=n,考虑能否把k那一维省掉
设g[i][j],i,j 含义同上,那么k<=n-j,
g[i][j]=∑n−jk=0f[i][j][k]
但是这样会算重,如果对于一组序列它最后为匹配的白色数为a,黑色为b
有a<=j && b<=n-j 且 a<=j’ && b<=n-j’
那么这一组序列在g[i][j]与g[i][j’]都会被计算
我们考虑当且仅当a=j 的时候计算,而对于a< j 且也合法的状态就不算了。这样只会被算一次
什么时候a=j呢,那么就应该是中间某个时刻出现了j=0(感受一下)
反证很显然:如果a>j,那么始终有j>0
于是多一维状态表示是否曾经出现了j=0,如果是才计入答案
状态O(n^2)转移O(1)
Code
#include<ctime>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v,u) for(int i=last[v],u=to[i];i;i=next[i],u=to[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
char ch;
void read(int &n)
{
n=0;int p=1;
for(ch=getchar();ch<'0' || ch>'9';ch=getchar())
if(ch=='-') p=-1;
for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';
n*=p;
}
const int N=3005;
int n,m,mo,f[N][N][2];
void update(int &x,ll y){x=(ll)(x+y)%mo;}
int main()
{
freopen("friend.in","r",stdin);
//freopen("friend.out","w",stdout);
read(n),read(m),read(mo);
--n,--m;
fo(i,0,n) f[0][i][(i==0)]=1;
fo(i,0,m)
{
update(f[i+1][0][1],2*f[i][0][1]);//0 0;1 1;
update(f[i+1][1][1],f[i][0][1]);//0 1
fo(j,1,n)
{
update(f[i+1][j][0],2*f[i][j][0]);
update(f[i+1][j][1],2*f[i][j][1]);
//0 0;1 1;
if(j<n)
{
update(f[i+1][j+1][0],f[i][j][0]);
update(f[i+1][j+1][1],f[i][j][1]);
}//0 1
if(j>1)
{
update(f[i+1][j-1][0],f[i][j][0]);
update(f[i+1][j-1][1],f[i][j][1]);
}
else update(f[i+1][j-1][1],f[i][j][0]+f[i][j][1]);
//1 0
}
}
int ans=0;
fo(i,0,n) update(ans,f[m][i][1]);
printf("%d",(ll)ans*4%mo);
return 0;
}