洛谷傳送門
BZOJ傳送門
題目描述
近日,谷歌研發的圍棋AI—AlphaGo以4:1的比分戰勝了曾經的世界冠軍李世石,這是人工智能領域的又一里程碑。
與傳統的搜索式AI不同,AlphaGo使用了最近十分流行的卷積神經網絡模型。在卷積神經網絡模型中,棋盤上每一塊特定大小的區域都被當做一個窗口。例如棋盤的大小爲,窗口大小爲,那麼棋盤中共有個窗口。此外,模型中預先設定了一些模板,模板的大小與窗口的大小是一樣的。
下圖展現了一個的棋盤和兩個的模板。
對於一個模板,只要棋盤中有某個窗口與其完全匹配,我們稱這個模板是被激活的,否則稱這個模板沒有被激活。
例如圖中第一個模板就是被激活的,而第二個模板就是沒有被激活的。我們要研究的問題是:對於給定的模板,有多少個棋盤可以激活它。爲
了簡化問題,我們拋開所有圍棋的基本規則,只考慮一個的棋盤,每個位置只能是黑子、白子或無子三種情況,換句話說,這樣的棋盤共有種。此外,我們會給出個的模板。
我們希望知道,對於每個模板,有多少種棋盤可以激活它。強調:模板一定是兩行的。
輸入輸出格式
輸入格式:
輸入數據的第一行包含四個正整數,,和,分別表示棋盤的行數、列數、模板的列數和模板的數量。
隨後行,每連續兩行描述一個模板。其中,每行包含個字符,字符一定是’W’,'B’或’X’中的一個,表示白子、黑子或無子三種情況的一種。
輸出格式:
輸出應包含行,每行一個整數,表示符合要求的棋盤數量。由於答案可能很大,你只需要輸出答案對取模後的結果即可。
輸入輸出樣例
輸入樣例#1:
3 1 1 2
B
W
B
B
輸出樣例#1:
6
5
解題分析
好難的DP啊QAQ
判重很麻煩, 轉化爲完全不存在的方案數。
顯然有個大暴力: 直接壓每行選擇的方式,然鵝這樣是, 完全過不了。
那麼怎麼優化狀態?發現對於當前行我們不關心上一行具體填的是什麼,只關心具體哪些位置可以匹配第一行的模板串,然後dp時記錄匹配到哪個位置, 這樣就可以做到大概, 仍然很涼。
繼續觀察不難得出, 假設現在考慮第行第列的位置, 那麼行列及之前的信息對我們完全沒有用。
所有可以維護一個輪廓線, 表示這些位置能否作爲一個匹配第一行模式串的結尾位置。 然後可能可以匹配的位置實際上每列只有個, 所以輪廓線長度也就是。
然後轉移可以通過預處理kmp, 枚舉字符做到。
代碼如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define MOD 1000000007
#define W while
#define gc getchar()
#define ll long long
int n, m, c, q, all;
const int SIZ = 1 << 12;
int nex1[10], nex2[10], to1[10][3], to2[10][3];
int dat1[10], dat2[10], dp[2][SIZ][7][7];
char str1[10], str2[10];
IN int give(R char c)
{
if (c == 'W') return 0;
if (c == 'B') return 1;
return 2;
}
IN int fpow(R int base, R int tim)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % MOD;
base = 1ll * base * base % MOD, tim >>= 1;
}
return ret;
}
IN void add(int &tar, R int ad) {tar += ad; if (tar >= MOD) tar -= MOD;}
int main(void)
{
scanf("%d%d%d%d", &n, &m, &c, &q);
all = 1 << m - c + 1;
R int s, a, b, col, i, j, now, cur, pre, p1, p2, nex;
W (q--)
{
pre = 0, cur = 1;
scanf("%s%s", str1 + 1, str2 + 1);
for (i = 1; i <= c; ++i)
dat1[i] = give(str1[i]), dat2[i] = give(str2[i]);
for (i = 2, now = 0; i <= c; ++i)
{
W (now && dat1[now + 1] != dat1[i]) now = nex1[now];
if (dat1[now + 1] == dat1[i]) ++now;
nex1[i] = now;
}
for (i = 2, now = 0; i <= c; ++i)
{
W (now && dat2[now + 1] != dat2[i]) now = nex2[now];
if (dat2[now + 1] == dat2[i]) ++now;
nex2[i] = now;
}
for (i = 0; i < c; ++i)
for (col = 0, now = i; col < 3; ++col, now = i)
{
W (now && dat1[now + 1] != col) now = nex1[now];
if (dat1[now + 1] == col) ++now;
to1[i][col] = now;
}
for (i = 0; i < c; ++i)
for (col = 0, now = i; col < 3; ++col, now = i)
{
W (now && dat2[now + 1] != col) now = nex2[now];
if (dat2[now + 1] == col) ++now;
to2[i][col] = now;
}
std::memset(dp, 0, sizeof(dp));
dp[pre][0][0][0] = 1;
for (i = 1; i <= n; ++i)
{
std::memset(dp[cur], 0, sizeof(dp[cur]));
for (s = 0; s < all; ++s)
for (a = 0; a < c; ++a)
for (b = 0; b < c; ++b)
if (dp[pre][s][a][b])
add(dp[cur][s][0][0], dp[pre][s][a][b]);
std::swap(pre, cur);
for (j = 1; j <= m; ++j)
{
std::memset(dp[cur], 0, sizeof(dp[cur]));
for (s = 0; s < all; ++s)
for (a = 0; a < c; ++a)
for (b = 0; b < c; ++b)
if (dp[pre][s][a][b])
{
for (R int col = 0; col < 3; ++col)
{
nex = s;
p1 = to1[a][col];
p2 = to2[b][col];
if (j >= c && ((s >> j - c) & 1)) nex ^= 1 << j - c;
if (p1 == c) nex ^= 1 << j - c, p1 = nex1[c];
if (p2 == c)
{
if ((s >> j - c) & 1) continue;
p2 = nex2[c];
}
add(dp[cur][nex][p1][p2], dp[pre][s][a][b]);
}
}
std::swap(cur, pre);
}
}
int ans = fpow(3, n * m);
for (s = 0; s < all; ++s)
for (a = 0; a < c; ++a)
for (b = 0; b < c; ++b)
add(ans, MOD - dp[pre][s][a][b]);
printf("%d\n", ans);
}
}