P3290 [SCOI2016]围棋

P3290 SCOI2016围棋

轮廓线DP + KMP
用轮廓线 DP 算出无法匹配的个数, 用 3**(n*m) 减去即可
..#### f[x][y][s][i][j] 表示 s 为轮廓线, 模板第一行匹配到 i, 模板第二行匹配到 j
##?.?.   s 第某位为 0/1 表示模板第一行是否能匹配到这一位
点击查看代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>

using namespace std;

typedef long long LL;

constexpr int mod = 1e9 + 7;

int n, m, c, T;
char str[2][15]; // 模板串
int mb[2][15]; // str 变成的 01 串
int nxt[2][15];    // KMP  nxt[][i] 表示 mb[1..t] = mb[i-t+1..i] 的最大 t (0≤t<i)
int tmp[2][15][3]; // KMP  tmp[][i][color] 表示在 i 后面加上 color 后匹配到的位置
int f[2][1 << 10][15][15]; // [x][y] => [0/1]

inline LL qpow(LL b, LL p) {
	LL res = 1;
	while(p) {
		if(p & 1) res = res * b % mod;
		b = b * b % mod, p >>= 1;
	}
	return res;
}

// 取模
inline void add(int &x, const int &y) {
	x += y;
	if(x >= mod) x -= mod;
}

int main() {
	scanf("%d%d%d%d", &n, &m, &c, &T);
	int all = qpow(3, n * m);
	while(T --) {
		scanf("%s%s", str[0] + 1, str[1] + 1);
		for(int t = 0; t < 2; t ++)
			for(int i = 1; i <= c; i ++)
				mb[t][i] = ((str[t][i] == 'X') ? 2 : (str[t][i] == 'B'));
		// KMP
		for(int t = 0; t < 2; t ++) {
			mb[t][0] = mb[t][c + 1] = -1; // 边界条件
			for(int i = 2, j = 0; i <= c; i ++) {
				while(j && mb[t][i] != mb[t][j + 1]) j = nxt[t][j];
				if(mb[t][i] == mb[t][j + 1]) j ++;
				nxt[t][i] = j;
			}
			for(int i = 0; i <= c; i ++) {
				for(int color = 0; color < 3; color ++) {
					int now = i;
					while(now && mb[t][now + 1] != color) now = nxt[t][now];
					if(mb[t][now + 1] == color) now ++;
					tmp[t][i][color] = now;
				}
			}
		}
		int full_set = 1 << (m - c + 1), now = 0;
		memset(f, 0, sizeof(f));
		f[0][0][0][0] = 1;
		for(int x = 1; x <= n; x ++) {
			for(int y = 1; y <= m; y ++) {
				memset(f[!now], 0, sizeof(f[!now]));
				for(int s = 0; s < full_set; s ++) {
					for(int a = 0; a <= c; a ++) {
						for(int b = 0; b <= c; b ++) {
							for(int color = 0; color < 3; color ++) {
								int nxts; // 新的 s
								int nxta = tmp[0][a][color]; // 新的匹配的位置 a
								int nxtb = tmp[1][b][color]; // 新的匹配的位置 b
								if(y < c) nxts = s; // 一定不会变
								else { // 是否匹配?
									nxts = s | (1 << (y - c)); // 匹配到了
									if(nxta != c) nxts ^= (1 << (y - c)); // 没有匹配到 -> 0
								}
								if(nxtb == c && (s >> (y - c) & 1)) continue; // 匹配到了 -> 不能转移
								if(y == m) // 下一行的情况
									add(f[!now][nxts][0][0], f[now][s][a][b]);
								else
									add(f[!now][nxts][nxta][nxtb], f[now][s][a][b]);
							}
						}
					}
				}
				now ^= 1;
			}
		}
		int res = 0;
		for(int i = 0; i < full_set; i ++) add(res, f[now][i][0][0]);
		printf("%d\n", (all - res + mod) % mod);
	}
	return 0;
}
posted @ 2022-09-28 14:31  azzc  阅读(28)  评论(0编辑  收藏  举报