21.5.12 t1
tag:组合计数,dp
规定一个顺序,先放完横着的,再对于每一种横着的情况去放斜着的。那么会导致重复的放置方案只有:
- 对于一个长度为 \(k\) 的斜操作,被连续 \(k\) 个横操作覆盖了。那么放与不放都会被计算。
- 存在连续 \(m\) 个斜操作。那么对应的第一行有无横操作都会被计算一次。
所以规定斜线的放置方案为:
-
对于一个长度为 \(k\) 的斜操作,若之前连续放了 \(k\) 个横操作,则不能放
-
对于连续 \(k\) 个斜操作,若对应的第一行没有横操作,则不能同时存在
于是设 \(f(i,j,k)\) 表示前 \(i\) 行,当前连续放了 \(j\) 个横操作,连续放了 \(k\) 个斜操作。枚举到第 \(i\) 行的时候,分别考虑
- 不操作
- 当前行涂黑
- \((i,1)\) 开始的斜线涂黑
- 行和斜线都涂黑
然后枚举到第 \(n\) 行的时候再单独考虑一下即可。
细节看代码。
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar())) if(ch=='-')flag=true;
for(n=ch^48; isdigit(ch=getchar()); n=(n<<1)+(n<<3)+(ch^48));
if(flag) n=-n;
}
enum{
MAXN = 505
};
int n, m, MOD;
inline int inc(int a, int b){
a += b;
if(a>=MOD) a -= MOD;
return a;
}
inline int dec(int a, int b){
a -= b;
if(a<0) a += MOD;
return a;
}
inline void iinc(int &a, int b){a = inc(a,b);}
inline void ddec(int &a, int b){a = dec(a,b);}
inline void upd(int &a, long long b){a = (a+b)%MOD;}
int f[MAXN][MAXN][MAXN], Pow2[MAXN];
int dp(int i, int j, int k){
if(~f[i][j][k]) return f[i][j][k];
int &res = f[i][j][k] = 0;
if(i==n){
// line
res = 1;
// cross
upd(res,1ll*Pow2[k]*dec(Pow2[m-k],1));
// line and cross
if(j+1<n){ // 特判最后一行不能放,只有n<m会出现这种情况(样例正好没有)
int lim = max(m-j-1,0);
if(lim<m-k) iinc(res,dec(Pow2[lim],1));
else iinc(res,max(0,dec(1ll*Pow2[lim-(m-k)]*dec(Pow2[m-k],1)%MOD,1)));
// 减1是因为不放cross与之前的line的case重复
}
return res;
}
// line
iinc(res,dp(i+1,min(m,j+1),0));
// cross
if(k+1!=m)
iinc(res,dp(i+1,0,k+1));
// empty
iinc(res,dp(i+1,0,0));
// line and cross
if(k+1!=m and min(i,m)>min(j+1,m))
if(!k) iinc(res,dp(i+1,min(m,j+1),0));
else iinc(res,dp(i+1,min(m,j+1),k+1));
return res;
}
int main(){
// freopen("coloring.out","w",stdout);
memset(f,-1,sizeof f);
Read(n); Read(m); Read(MOD);
Pow2[0] = 1; for(register int i=1; i<=500; i++) Pow2[i] = inc(Pow2[i-1],Pow2[i-1]);
cout<<dp(1,0,0)<<'\n';
// for(register int i=1; i<=n; i++) for(register int j=0; j<=n; j++) for(register int k=0; k<=m; k++) printf("f(%d %d %d) = %d\n",i,j,k,f[i][j][k]);
return 0;
}