【省选模拟】那些年我们画格子(DP)(数数)

在这里插入图片描述

  • 挺有意思的数数题
  • 首先需要推一些性质:
    每一个四宫格的颜色各部相同,易证
    所有的合法矩阵满足下面条件之一:
    每一行只有两种颜色,相邻两行不同,每一列只有两种颜色,相邻两列不同
  • 于是我们对行列分别求一遍把行列都满足的容斥掉。
    那么我们枚举奇数行的两种颜色,求出这种填法下这一行下修改的个数
    f i , j f_{i,j} fi,j 表示到第 i i i 排改了 k k k 个位置的方案数就可以 d p dp dp
    复杂度 O ( n k ) O(nk) O(nk) n = 1 n=1 n=1 或者 m = 1 m=1 m=1 的情况要特判掉
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int K = 16, N = 105, M = 1e4 + 50;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
void Add(int &a, int b){ a = add(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
int n, m, k, mp[N][N];
char S[N];
int idx(char c){ 
	if(c == 'R') return 0;
	if(c == 'G') return 1;
	if(c == 'B') return 2;
	if(c == 'Y') return 3;
}
namespace Main{
	int ans, p[K], num[K][N], dp[N][M]; bool vis[K];
	void matrix_work(int u, int v){
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) 
		if(mp[i][j] != ((j&1)?u:v)) ++num[u*4+v][i];
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) 
		if(mp[i][j] != ((j&1)?v:u)) ++num[v*4+u][i];
	}
	void work(int A, int B, int C, int D, bool FLAG, int coe){
		for(int i = 0; i <= n; i++) 
		for(int j = 0; j <= k; j++) dp[i][j] = 0;
		dp[0][0] = 1;
		for(int i = 1; i <= n; i++){
			int u = A, v = B;
			if(i & 1 ^ 1) u = C, v = D;
			int now = num[u*4+v][i];
			for(int j = now; j <= k; j++) Add(dp[i][j],dp[i-1][j-now]);
			if(!FLAG){
				int now = num[v*4+u][i];
				for(int j = now; j <= k; j++) Add(dp[i][j],dp[i-1][j-now]);
			}
		}
		for(int i = 0; i <= k; i++) 
			(coe==1) ? Add(ans,dp[n][i]) : Dec(ans,dp[n][i]);
	}
	void dfs(int u){
		if(u > 4){ work(p[1],p[2],p[3],p[4],1,-1); return; }
		for(int i = 0; i < 4; i++) if(!vis[i]) 
			p[u]=i, vis[i]=true, dfs(u+1), vis[i]=false;
	}
	void matrix_rotate(){
		static int tmp[N][N];
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) tmp[j][i] = mp[i][j];
		swap(n, m);
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) mp[i][j] = tmp[i][j];
	}
	void Solve(){
		matrix_work(0,1);
		matrix_work(0,2);
		matrix_work(0,3);
		matrix_work(1,2);
		matrix_work(1,3);
		matrix_work(2,3);
		work(0,1,2,3,0,1);
		work(0,2,1,3,0,1);
		work(0,3,1,2,0,1);
		work(1,2,0,3,0,1);
		work(1,3,0,2,0,1);
		work(2,3,0,1,0,1);
		dfs(1);
		matrix_rotate();
		memset(num, 0, sizeof(num));
		matrix_work(0,1);
		matrix_work(0,2);
		matrix_work(0,3);
		matrix_work(1,2);
		matrix_work(1,3);
		matrix_work(2,3);
		work(0,1,2,3,0,1);
		work(0,2,1,3,0,1);
		work(0,3,1,2,0,1);
		work(1,2,0,3,0,1);
		work(1,3,0,2,0,1);
		work(2,3,0,1,0,1);
		cout << ans; 
	}
}
namespace Special{
	int dp[N][N][4], ans;
	void Solve(){
		if(m == 1) Main::matrix_rotate();
		for(int i = 0; i < 4; i++) dp[1][i!=mp[1][i]][i] = 1;
		for(int i = 2; i <= m; i++)
		for(int j = 0; j <= k; j++)
		for(int l = 0; l < 4; l++) 
		for(int r = 0; r < 4; r++) if(l ^ r) 
		Add(dp[i][j+(r!=mp[1][i])][r],dp[i-1][j][l]);
		for(int i = 0; i <= k; i++) 
		for(int l = 0; l < 4; l++) Add(ans, dp[m][i][l]);
		cout << ans;
	}
} 
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1; i<=n; i++){
		scanf("%s",S+1);
		for(int j=1; j<=m; j++) mp[i][j]=idx(S[j]);
	} 
	if(n>1&&m>1){ Main::Solve(); return 0; }
	Special::Solve(); return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值