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;
}
posted @ 2021-06-25 09:39  oisdoaiu  阅读(25)  评论(0编辑  收藏  举报